From 6c2f030243728572f24e8be1d02ca04ac9e56e01 Mon Sep 17 00:00:00 2001 From: "Timothy J. Aveni" Date: Mon, 14 Sep 2026 20:35:53 -0700 Subject: [PATCH 01/11] Simplify workspace authoring and isolate managed component styles Use imperative scaffolds without a registry/refresh lifecycle; preserve authored runtime wiring and generate checked descriptors. Consolidate binding options, embed checked TSX/CSS source, queue mutable convergence while allowing parallel immutable checks, and preload real CLI observations. Add Shadow DOM boundaries with scoped overlays and composed-event handling. Update template/toolchain pins and document VM-free authoring, browser, and stateful acceptance; no deployment or default-template selection. --- package.json | 5 +- src/bindings/bundle-policy.ts | 41 ++++++++ src/bindings/index.ts | 6 ++ src/capability-language/authoring-check.ts | 7 ++ .../implementation-edit.ts | 29 ++++++ src/capability-language/migration-seal.ts | 23 +++++ src/capability-language/scaffold-recipes.ts | 93 +++++++++---------- src/capability-language/structural-plan.ts | 20 +--- src/capability-language/tool-cli.ts | 54 +++++++++-- test/bundle-policy.test.ts | 21 +++++ test/scaffold-recipes.test.ts | 34 +++---- yarn-project.nix | 4 + yarn.lock | 36 +++++++ 13 files changed, 278 insertions(+), 95 deletions(-) create mode 100644 src/bindings/bundle-policy.ts create mode 100644 src/capability-language/implementation-edit.ts create mode 100644 src/capability-language/migration-seal.ts create mode 100644 test/bundle-policy.test.ts diff --git a/package.json b/package.json index f16822c..bb98102 100644 --- a/package.json +++ b/package.json @@ -5,11 +5,11 @@ "packageManager": "yarn@4.18.0", "type": "module", "bin": { - "quixos-qx": "dist/src/capability-language/tool-cli.js", - "quixos-codegen-ts": "dist/src/bindings/cli.js", "quixos-capability-compile": "dist/src/capability-language/cli.js", + "quixos-codegen-ts": "dist/src/bindings/cli.js", "quixos-descriptor-check": "dist/src/descriptor-check.js", "quixos-lock-check": "dist/src/resource-lock/cli.js", + "quixos-qx": "dist/src/capability-language/tool-cli.js", "quixos-resource-compile": "dist/src/capability-language/resource-cli.js", "quixos-workspace-compile": "dist/src/capability-language/workspace-cli.js" }, @@ -29,6 +29,7 @@ "typecheck": "yarn generate && tsc --noEmit" }, "dependencies": { + "@babel/parser": "^7.28.0", "@bufbuild/protobuf": "^2.12.1", "@bufbuild/protoc-gen-es": "^2.12.1", "antlr4ng": "^3.0.16" diff --git a/src/bindings/bundle-policy.ts b/src/bindings/bundle-policy.ts new file mode 100644 index 0000000..c63e82f --- /dev/null +++ b/src/bindings/bundle-policy.ts @@ -0,0 +1,41 @@ +import {parse} from "@babel/parser"; +import fs from "node:fs/promises"; +import path from "node:path"; + +/** Enforce the authored bundled-code contract, not a security sandbox. */ +export function bundlePolicyErrors(source: string, filename: string): string[] { + const ast = parse(source, {sourceType: "module", plugins: ["typescript", "jsx"]}); + const errors: string[] = []; + const visit = (value: unknown) => { + if (Array.isArray(value)) {value.forEach(visit); return;} + if (!value || typeof value !== "object") return; + const n = value as Record; + if (typeof n.type !== "string") return; + const fail = (message: string) => errors.push(`${filename}:${n.loc?.start.line ?? 1}: ${message}`); + if (n.type === "MetaProperty" && n.meta.name === "import") fail("Bundled package code cannot use import.meta; import packaged assets statically"); + if (n.type === "Identifier" && ["__dirname", "__filename"].includes(n.name)) fail("Bundled package code cannot depend on module filesystem locations"); + if (["CallExpression", "NewExpression"].includes(n.type)) { + if (n.callee?.type === "Identifier" && ["eval", "Function"].includes(n.callee.name)) fail("Dynamic code generation is unsupported in bundled package code"); + if ((n.callee?.type === "Import" || (n.callee?.type === "Identifier" && n.callee.name === "require")) && + (n.arguments.length !== 1 || n.arguments[0].type !== "StringLiteral")) fail("Module imports must have a static string specifier"); + } + if (n.type === "ImportExpression" && n.source.type !== "StringLiteral") fail("Module imports must have a static string specifier"); + Object.values(n).forEach(visit); + }; + visit(ast); + return errors; +} +export async function checkBundleSources(directory: string): Promise { + const errors: string[] = []; + async function walk(current: string) { + for (const entry of await fs.readdir(current, {withFileTypes: true})) { + if (["gen", "node_modules"].includes(entry.name)) continue; + const file = path.join(current, entry.name); + if (entry.isSymbolicLink()) throw new Error(`Authored source symlinks are unsupported: ${file}`); + if (entry.isDirectory()) await walk(file); + else if (/\.[cm]?[jt]sx?$/.test(entry.name) && !entry.name.endsWith(".d.ts")) errors.push(...bundlePolicyErrors(await fs.readFile(file, "utf8"), file)); + } + } + await walk(directory); + if (errors.length) throw new Error(errors.join("\n")); +} diff --git a/src/bindings/index.ts b/src/bindings/index.ts index ba87ac4..0eda305 100644 --- a/src/bindings/index.ts +++ b/src/bindings/index.ts @@ -18,6 +18,12 @@ export type TypeScriptBindingOptions = { /** Each export must implement MessageBinding, providing both TS type and wire codec. */ messages?: Record; }; +export function generatePackageDescriptor(schema: BindingSchema, revisionId: string): string { + const pkg = schema.packages.find(entry => entry.revisionId === revisionId); + if (!pkg) throw new Error(`Unknown package revision ${revisionId}`); + return `# Generated from the checked package contract\npackage_id: ${JSON.stringify(pkg.packageId)}\npackage_revision_id: ${JSON.stringify(pkg.revisionId)}\nruntime_protocol_version: "quixos-capabilities-v1"\n` + + pkg.exports.map(entry => `exports: { export_id: ${JSON.stringify(entry.id)} runtime_symbol: ${JSON.stringify(entry.displayName)} }\n`).join(""); +} const q = JSON.stringify; const object = (entries: [string, string][]) => `{ ${entries.map(([key, value]) => `${q(key)}: ${value}`).join("; ")} }`; const unit = (type: ValueType) => type.kind === "builtin" && type.name === "unit"; diff --git a/src/capability-language/authoring-check.ts b/src/capability-language/authoring-check.ts index 73a3de0..656bab8 100644 --- a/src/capability-language/authoring-check.ts +++ b/src/capability-language/authoring-check.ts @@ -10,6 +10,8 @@ import { planEvolution, type WorkspaceRevision, type EvolutionReview } from "../ export const checkRecordName = (directory: string) => createHash("sha256").update(directory).digest("hex") + ".json"; export async function checkAuthoring(start: string, output: string, options: { baseline?: string; reviews?: string; contractOnly?: boolean } = {}) { + const started = performance.now(); + const timings: Record = {}; const context = await authoringContext(start); const location = await fs.realpath(start); const directory = location === context.workbench ? "root" : path.relative(context.workbench, location); @@ -29,13 +31,16 @@ export async function checkAuthoring(start: string, output: string, options: { b throw error; }); const converged = JSON.parse(captured.stdout) as Awaited>; + timings.captureMs = Math.round(performance.now() - started); if (!converged.candidate) { report.phase = converged.worklist.find(entry => entry.phase !== "dependency")?.phase ?? "convergence"; throw new Error(converged.worklist.map(entry => `${entry.directory} [${entry.phase}]: ${entry.message}`).join("\n")); } report.commit = converged.candidate.commit; report.phase = "verification"; + const buildStarted = performance.now(); report.artifactPath = await buildImmutableCandidate(converged.candidate, resource.kind, path.join(output, "nix.log"), options.contractOnly); + timings.immutableCheckMs = Math.round(performance.now() - buildStarted); const candidateText = await fs.readFile(path.join(report.artifactPath, "candidate.json"), "utf8"); await fs.writeFile(path.join(output, "candidate.json"), candidateText); report.compilation = "passed"; @@ -59,6 +64,8 @@ export async function checkAuthoring(start: string, output: string, options: { b } if (!report.blockers.length) report.phase = options.contractOnly ? "contract-only" : "checked"; } catch (error) { report.blockers.push(String(error instanceof Error ? error.message : error)); } + timings.totalMs = Math.round(performance.now() - started); + Object.assign(report, {timings}); await fs.writeFile(path.join(output, "report.json"), JSON.stringify(report, null, 2)); if (options.contractOnly) return report; const records = path.join(context.workbench, ".quixos/checks"); diff --git a/src/capability-language/implementation-edit.ts b/src/capability-language/implementation-edit.ts new file mode 100644 index 0000000..abc9962 --- /dev/null +++ b/src/capability-language/implementation-edit.ts @@ -0,0 +1,29 @@ +import {parse} from "@babel/parser"; +type Node = {type: string; start: number; end: number; [key: string]: unknown}; +const node = (value: unknown): value is Node => !!value && typeof value === "object" && typeof (value as Node).type === "string"; + +/** One requested insertion into ordinary authored TypeScript, not regeneration. + * Ambiguous/custom wiring is left alone with an actionable error. */ +export function addImplementation(text: string, factory: "createRuntime" | "serveMigration", key: string, importPath: string, migrationOnly = false): string { + const ast = parse(text, {sourceType: "module", plugins: ["typescript"]}); + const objects: Node[] = []; + const names = new Set(); + const visit = (value: unknown) => { + if (Array.isArray(value)) { value.forEach(visit); return; } + if (!node(value)) return; + if (value.type === "Identifier") names.add(String(value.name)); + if (value.type === "CallExpression" && node(value.callee) && value.callee.type === "Identifier" && value.callee.name === factory && + Array.isArray(value.arguments) && value.arguments.length === 1 && node(value.arguments[0]) && value.arguments[0].type === "ObjectExpression") objects.push(value.arguments[0]); + Object.values(value).forEach(visit); + }; + visit(ast); + if (objects.length !== 1) throw new Error(`Cannot safely add implementation: expected one ${factory}({...}) literal. Wire the handler in your authored server code instead.`); + const object = objects[0]; + if ((object.properties as Node[]).some(p => node(p.key) && !p.computed && (p.key.name === key || p.key.value === key))) throw new Error(`Implementation already exists for ${key}`); + let alias = "qxImplementation"; + for (let index = 1; names.has(alias); index++) alias = `qxImplementation${index}`; + const value = migrationOnly ? 'async () => { throw new Error("Migration-only export"); }' : alias; + const insertion = object.start + 1; + const edited = text.slice(0, insertion) + `\n ${JSON.stringify(key)}: ${value},\n` + text.slice(insertion); + return (migrationOnly ? "" : `import {handler as ${alias}} from ${JSON.stringify(importPath)};\n`) + edited; +} diff --git a/src/capability-language/migration-seal.ts b/src/capability-language/migration-seal.ts new file mode 100644 index 0000000..0fa196e --- /dev/null +++ b/src/capability-language/migration-seal.ts @@ -0,0 +1,23 @@ +import fs from "node:fs/promises"; +import path from "node:path"; +import {contentDigest} from "../capability-model/evolution.js"; +import {validateMigrationCatalog, type MigrationCatalog} from "../capability-model/migrations.js"; +import {applyStructure, planStructure} from "./structural-plan.js"; + +/** Explicitly acknowledge edited migration code. This does not change retained + * contracts or grant activation approval; the immutable checker verifies it. */ +export async function sealMigrations(directory: string) { + const root = await fs.realpath(directory); + const file = "quixos.migrations.json"; + const before = await fs.readFile(path.join(root, file), "utf8"); + const catalog = JSON.parse(before) as MigrationCatalog; + for (const migration of catalog.migrations) { + const implementation = await fs.realpath(path.join(root, migration.implementation.file)); + if (!implementation.startsWith(root + path.sep)) throw new Error("Migration implementation escapes package"); + migration.implementation.digest = contentDigest(await fs.readFile(implementation, "utf8")); + } + validateMigrationCatalog(catalog); + return applyStructure(await planStructure(root, {kind: "package", validation: "syntax", files: [ + {file, expected: before, replace: JSON.stringify(catalog, null, 2) + "\n"}, + ]})); +} diff --git a/src/capability-language/scaffold-recipes.ts b/src/capability-language/scaffold-recipes.ts index 70f50e3..057f3d3 100644 --- a/src/capability-language/scaffold-recipes.ts +++ b/src/capability-language/scaffold-recipes.ts @@ -5,9 +5,10 @@ import {validateMigrationCatalog, type MigrationCatalog, type MigrationDeclarati import {formatQuixosLock, type GitSource} from "../resource-lock/index.js"; import {parseQx, walkSyntax} from "./source.js"; import type {StructuralRequest} from "./structural-plan.js"; +import {addImplementation} from "./implementation-edit.js"; type Source = {repository: string; commit: string}; -type Registry = {generatedBy: "qx-scaffold-v1"; name: string; id: string; revision: string; exports: {name: string; id: string; file: string; migration?: boolean}[]}; +type PackageEditModel = {generatedBy: "qx-scaffold-v1"; name: string; id: string; revision: string; exports: {name: string; id: string; file: string; migration?: boolean}[]}; export type ScaffoldRecipe = { template?: "typescript" | "typescript-react"; source: Source; directory?: string; name?: string; id?: string; revision?: string; @@ -16,9 +17,9 @@ export type ScaffoldRecipe = { nixifyPluginUrl?: string; migration?: Omit & {contracts: Record}; }; -const marker = "// Generated by qx-scaffold-v1\n"; const json = (value: unknown) => `${JSON.stringify(value, null, 2)}\n`; const source = (value: Source): GitSource => { + if (!value || typeof value.repository !== "string" || typeof value.commit !== "string") throw new Error("Scaffold requires an exact source identity; use qx-workspace from a registered repository"); const url = new URL(value.repository); if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash || !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(value.commit)) throw new Error("Scaffolds require credential-free HTTPS sources and full exact commits"); return {resolver: "git", ...value}; @@ -38,15 +39,16 @@ const ownedJson = async (root: string, file: string): Promise => { }; /** Recipes describe structural edits; planStructure owns validation/journaling. - * Implementation files are created once and never rewritten by refresh. */ + * Implementation files are created once; later edits preserve authored wiring. */ export const scaffoldRecipe = async (root: string, command: "package" | "function" | "migration" | "refresh", spec: ScaffoldRecipe): Promise => { + if (command === "refresh") throw new Error("Scaffold refresh has been removed. Edit declarations and typed server wiring directly, then run qx-workspace check. Use scaffold function to add a declaration and handler together."); source(spec.source); if (spec.directory && !/^[A-Za-z0-9_-][A-Za-z0-9_.-]*(?:\/[A-Za-z0-9_-][A-Za-z0-9_.-]*)*$/.test(spec.directory)) throw new Error("Scaffold directory must be contained"); const prefix = spec.directory ? `${spec.directory}/` : ""; const files: StructuralRequest["files"] = []; const create = (file: string, content: string) => files.push({file: prefix + file, create: content}); const generated = (file: string, content: string) => files.push({file: prefix + file, generated: content}); - let registry: Registry; + let packageModel: PackageEditModel; let catalog: MigrationCatalog & {generatedBy: "qx-scaffold-v1"}; if (command === "package") { const name = safeName(spec.name); @@ -54,19 +56,19 @@ export const scaffoldRecipe = async (root: string, command: "package" | "functio const react = spec.template === "typescript-react"; if (!spec.id || !spec.revision || !spec.tools) throw new Error("Package scaffold requires id, revision, and exact quixos/protocol/helpers/sdk tool sources"); Object.values(spec.tools).forEach(source); - registry = {generatedBy: "qx-scaffold-v1", name, id: spec.id, revision: spec.revision, exports: []}; + packageModel = {generatedBy: "qx-scaffold-v1", name, id: spec.id, revision: spec.revision, exports: []}; catalog = {generatedBy: "qx-scaffold-v1", schemaVersion: 1, contracts: {}, migrations: []}; - if (react) registry.exports.push({name: "sourceGet", id: `export:${name}:source`, file: "src/impl/sourceGet.ts"}); + if (react) packageModel.exports.push({name: "sourceGet", id: `export:${name}:source`, file: "src/impl/sourceGet.ts"}); create("package.qx", `package ${name} id ${JSON.stringify(spec.id)} revision ${JSON.stringify(spec.revision)} {\n${react ? ` function sourceGet id ${JSON.stringify(`export:${name}:source`)} : unit -> string;\n` : ""}}\n`); create("quixos.lock", formatQuixosLock({formatVersion: 1, quixos: source(spec.tools.quixos), resources: []})); create("package.json", json({name: `@quixos/${name.toLowerCase()}`, version: "0.1.0", private: true, type: "module", packageManager: "yarn@4.18.0", - scripts: {build: `tsc -p tsconfig.json${react ? " && node scripts/build-component.mjs" : ""}`, typecheck: "tsc --noEmit"}, dependencies: {"@quixos/camino-package-runtime": `${spec.tools.sdk.repository}#commit=${spec.tools.sdk.commit}`}, + scripts: {build: "tsc -p tsconfig.json", typecheck: "tsc --noEmit"}, dependencies: {"@quixos/camino-package-runtime": `${spec.tools.sdk.repository}#commit=${spec.tools.sdk.commit}`}, devDependencies: {"@types/node": "^24", typescript: "^7.0.2", ...(react ? {react: "^18.3.1", "@types/react": "^18.3.12", esbuild: "^0.25.12"} : {})}})); create("tsconfig.json", json({compilerOptions: {target: "ES2023", module: "NodeNext", moduleResolution: "NodeNext", strict: true, types: ["node", ...(react ? ["react"] : [])], outDir: "dist", rootDir: "src", skipLibCheck: true, ...(react ? {jsx: "react-jsx", esModuleInterop: true} : {})}, include: ["src/**/*.ts", "src/**/*.tsx"]})); if (react) { create("src/component.tsx", `// Props are opaque at the platform boundary until capability generics exist.\nexport default function Component(_props: {camino: unknown; render: unknown; dispatch: (action: unknown) => void}) {\n return

${name}

Edit this component, then run qx-workspace check.

;\n}\n`); - create("src/impl/sourceGet.ts", `import {readFile} from "node:fs/promises";\nimport type {Implementation} from "../gen/qx.js";\nexport const handler: Implementation["sourceGet"] = () => readFile(new URL("./component.mjs", import.meta.url), "utf8");\n`); - create("scripts/build-component.mjs", `import {build} from "esbuild";\nconst platform = new Map([\n ["react", "/__quixos/platform/react/v18.mjs"],\n ["react/jsx-runtime", "/__quixos/platform/react-jsx-runtime/v18.mjs"],\n ["react/jsx-dev-runtime", "/__quixos/platform/react-jsx-dev-runtime/v18.mjs"],\n ["@quixos/web-studio-react-runtime", "/__quixos/platform/web-studio-react-runtime/v1.mjs"],\n]);\nawait build({entryPoints: ["dist/component.js"], outfile: "dist/component.mjs", bundle: true, format: "esm", platform: "browser", target: "es2022", plugins: [{name: "quixos-platform", setup(api) {api.onResolve({filter: /.*/}, ({path}) => platform.has(path) ? {path: platform.get(path), external: true} : undefined);}}]});\n`); + create("src/impl/sourceGet.ts", `import componentSource from "../component.js?browser-source";\nimport type {Implementation} from "../gen/qx.js";\nexport const handler: Implementation["sourceGet"] = () => componentSource;\n`); + create("src/browser-assets.d.ts", `declare module "*?browser-source" { const source: string; export default source; }\n`); } create(".gitignore", "node_modules/\ndist/\n.quixos/\nresult\n.yarn/install-state.gz\n"); create(".yarnrc.yml", `nodeLinker: node-modules\nenableScripts: true\nnpmMinimalAgeGate: 0\napprovedGitRepositories:\n - ${JSON.stringify(spec.tools.sdk.repository)}\nsupportedArchitectures:\n os: [current, linux]\n cpu: [current, x64, arm64]\n libc: [current, glibc]\n`); @@ -74,7 +76,7 @@ export const scaffoldRecipe = async (root: string, command: "package" | "functio const plugin = new URL(nixifyPluginUrl); if (plugin.protocol !== "https:" || plugin.username || plugin.password || plugin.search || plugin.hash || !/\/commit\/[a-f0-9]{40,64}\//.test(plugin.pathname)) throw new Error("Nixify plugin must have an exact credential-free HTTPS commit URL"); generated("quixos.toolchain.json", json({generatedBy: "qx-scaffold-v1", nixifyPluginUrl})); - create("quixos.check.json", json({backend: "typescript", bindingOutput: "src/gen/qx.ts"})); + create("quixos.check.json", json({backend: "typescript", bindingOutput: "src/gen/qx.ts", ...(react ? {options: {messages: {"org.quixos.web-studio.ReactProps": {module: "@quixos/camino-package-runtime", export: "opaqueReactPropsBinding"}}}} : {})})); create("flake.nix", `{ inputs.protocol.url = ${nixString(nixSource(spec.tools.protocol))}; inputs.nixpkgs.follows = "protocol/nixpkgs"; @@ -83,47 +85,34 @@ export const scaffoldRecipe = async (root: string, command: "package" | "functio outputs = inputs@{ self, protocol, nixpkgs, flake-utils, helpers, ... }: (import (toString helpers + "/quixos-package-helpers.nix")).mkCaminoTsYarnNixifyFlake { inherit inputs nixpkgs flake-utils; packageRoot = ./.; - bundle = { entry = "dist/server.js"; }; + bundle = { entry = ${JSON.stringify(react ? "src/server.ts" : "dist/server.js")}; ${react ? "browserSources = true;" : ""} }; migrationEntrypoint = "dist/migrate.js"; - installServer = { libexecName = ${JSON.stringify(name.toLowerCase())}; descriptorPath = "descriptor.quixos-package.txtpb"; ${react ? 'extraFiles = [ { source = "dist/component.mjs"; target = "component.mjs"; } ];' : ""} }; + installServer = { libexecName = ${JSON.stringify(name.toLowerCase())}; descriptorPath = "descriptor.quixos-package.txtpb"; }; }; }\n`); } else { - registry = await ownedJson(root, prefix + "quixos.scaffold.json"); catalog = await ownedJson(root, prefix + "quixos.migrations.json"); - // package.qx is authoritative. The registry remembers implementation paths, - // not a second declaration list that can erase an author's new exports. + // Derive the edit model from authored declarations; it is never persisted. const authored = await fs.readFile(path.join(root, prefix, "package.qx"), "utf8"); const syntax = parseQx(authored); - if (syntax.diagnostics.length) throw new Error("Cannot refresh an invalid package.qx; fix the reported syntax first"); + if (syntax.diagnostics.length) throw new Error("Cannot scaffold into an invalid package.qx; fix the reported syntax first"); const declaration = [...walkSyntax(syntax.root)].find(node => node.kind === "packageResourceDecl"); if (!declaration) throw new Error("Expected a package declaration"); const text = (node: typeof declaration) => authored.slice(node.start, node.end); const literals = declaration.children.filter(node => node.kind === "stringLiteral"); - registry.name = text(declaration.children.find(node => node.kind === "identifier")!); - registry.id = JSON.parse(text(literals[0])); - registry.revision = JSON.parse(text(literals[1])); - const previousExports = registry.exports; - registry.exports = []; + packageModel = {generatedBy: "qx-scaffold-v1", name: text(declaration.children.find(node => node.kind === "identifier")!), + id: JSON.parse(text(literals[0])), revision: JSON.parse(text(literals[1])), exports: []}; for (const node of walkSyntax(declaration)) { if (!["packageFunctionExport", "packageOperationExport", "packageConstructorExport"].includes(node.kind)) continue; const name = safeName(text(node.children.find(child => child.kind === "identifier")!)); const id = JSON.parse(text(node.children.find(child => child.kind === "stringLiteral")!)); - if (registry.exports.some(entry => entry.id === id || entry.name === name)) throw new Error("Duplicate package export name or ID"); - const old = previousExports.find(entry => entry.id === id); - const file = old?.file ?? `src/impl/${name}.ts`; - if (!old) { - const exists = await fs.access(path.join(root, prefix, file)).then(() => true, () => false); - if (!exists) { - const derived = [...walkSyntax(node)].some(child => child.kind === "eventClause"); - create(file, `import type {Implementation} from "../gen/qx.js";\nexport const handler: Implementation[${JSON.stringify(name)}] = ${derived ? '{kind: "derived", get: ' : ""}async (_context) => { throw new Error(${JSON.stringify(`Implement ${name}`)}); }${derived ? "}" : ""};\n`); - } - } - registry.exports.push({...old, name, id, file}); + if (packageModel.exports.some(entry => entry.id === id || entry.name === name)) throw new Error("Duplicate package export name or ID"); + const migration = catalog.migrations.find(entry => entry.implementation.exportId === id); + packageModel.exports.push({name, id, file: migration?.implementation.file ?? `src/impl/${name}.ts`, ...(migration ? {migration: true} : {})}); } - if (command !== "refresh") { + { const name = safeName(spec.name); - if (!spec.id || registry.exports.some((entry) => entry.id === spec.id || entry.name === name)) throw new Error("New export requires a unique name and ID"); + if (!spec.id || packageModel.exports.some((entry) => entry.id === spec.id || entry.name === name)) throw new Error("New export requires a unique name and ID"); const declaration = spec.declaration ?? `function ${name} id ${JSON.stringify(spec.id)} : unit -> unit;`; const parsed = parseQx(`package Scaffold id "scaffold" revision "scaffold@1" { ${declaration} }`); const exports = [...walkSyntax(parsed.root)].filter((node) => ["packageFunctionExport", "packageOperationExport", "packageConstructorExport"].includes(node.kind)); @@ -134,12 +123,12 @@ export const scaffoldRecipe = async (root: string, command: "package" | "functio if (command === "migration" && (node.kind !== "packageFunctionExport" || spec.declaration)) throw new Error("Migration exports use the scaffold's unit function declaration and dedicated migration entrypoint"); if (wrapped.slice(node.children.find((child) => child.kind === "identifier")!.start, node.children.find((child) => child.kind === "identifier")!.end) !== name || JSON.parse(wrapped.slice(node.children.find((child) => child.kind === "stringLiteral")!.start, node.children.find((child) => child.kind === "stringLiteral")!.end)) !== spec.id) throw new Error("Declaration name/ID must match its registration"); - files.push({file: prefix + "package.qx", edits: [{operation: "append", parent: {kind: "packageResourceDecl", id: registry.id}, source: declaration}]}); + files.push({file: prefix + "package.qx", edits: [{operation: "append", parent: {kind: "packageResourceDecl", id: packageModel.id}, source: declaration}]}); const file = `src/${command === "migration" ? "migrations" : "impl"}/${name}.ts`; const implementation = command === "migration" ? `import type {MigrationContext} from "@quixos/camino-package-runtime";\nexport const handler = async (_context: MigrationContext): Promise => { throw new Error(${JSON.stringify(`Implement migration ${name}`)}); };\n` : `import type {Implementation} from "../gen/qx.js";\nexport const handler: Implementation[${JSON.stringify(name)}] = ${derived ? '{kind: "derived", get: ' : ""}async (_context) => { throw new Error(${JSON.stringify(`Implement ${name}`)}); }${derived ? "}" : ""};\n`; create(file, implementation); - registry.exports.push({name, id: spec.id, file, ...(command === "migration" ? {migration: true} : {})}); + packageModel.exports.push({name, id: spec.id, file, ...(command === "migration" ? {migration: true} : {})}); if (command === "migration") { if (!spec.migration) throw new Error("Migration scaffold requires retained contracts and an explicit transition"); const {contracts, ...transition} = spec.migration; @@ -150,19 +139,25 @@ export const scaffoldRecipe = async (root: string, command: "package" | "functio catalog.migrations.push({...transition, implementation: {exportId: spec.id, file, digest: contentDigest(implementation)}}); } } - if (command === "refresh") for (const migration of catalog.migrations) { - const file = path.join(root, prefix, migration.implementation.file); - if (!(await fs.realpath(file)).startsWith(`${await fs.realpath(path.join(root, prefix))}/`)) throw new Error("Migration implementation escapes package"); - migration.implementation.digest = contentDigest(await fs.readFile(file, "utf8")); - } } - validateMigrationCatalog(catalog, new Set(registry.exports.map((entry) => entry.id))); - generated("quixos.scaffold.json", json(registry)); + validateMigrationCatalog(catalog, new Set(packageModel.exports.map((entry) => entry.id))); generated("quixos.migrations.json", json(catalog)); - generated("src/server.ts", marker + `import {servePackageRuntime} from "@quixos/camino-package-runtime";\nimport {createRuntime} from "./gen/qx.js";\n` + registry.exports.filter((entry) => !entry.migration).map((entry, index) => `import {handler as impl${index}} from ${JSON.stringify(`./${entry.file.slice(4, -3)}.js`)};\n`).join("") + - `servePackageRuntime(createRuntime({\n` + registry.exports.map((entry) => ` ${JSON.stringify(entry.name)}: ${entry.migration ? 'async () => { throw new Error("Migration-only export"); }' : `impl${registry.exports.filter((value) => !value.migration).indexOf(entry)}`},`).join("\n") + `\n}));\n`); - const migrations = registry.exports.filter((entry) => entry.migration); - generated("src/migrate.ts", marker + `import {serveMigration} from "@quixos/camino-package-runtime";\n` + migrations.map((entry, index) => `import {handler as impl${index}} from ${JSON.stringify(`./${entry.file.slice(4, -3)}.js`)};\n`).join("") + `await serveMigration({${migrations.map((entry, index) => `${JSON.stringify(entry.id)}: impl${index}`).join(", ")}});\n`); - generated("descriptor.quixos-package.txtpb", `# Generated by qx-scaffold-v1\npackage_id: ${JSON.stringify(registry.id)}\npackage_revision_id: ${JSON.stringify(registry.revision)}\nruntime_protocol_version: "quixos-capabilities-v1"\n` + registry.exports.map((entry) => `exports: { export_id: ${JSON.stringify(entry.id)} runtime_symbol: ${JSON.stringify(entry.name)} }\n`).join("")); - return {kind: "package", source: spec.source, resourceRoot: spec.directory, files}; + if (command !== "package") { + const entry = packageModel.exports.at(-1)!; + const file = prefix + "src/server.ts"; + const before = await fs.readFile(path.join(root, file), "utf8"); + files.push({file, expected: before, replace: addImplementation(before, "createRuntime", entry.name, `./${entry.file.slice(4, -3)}.js`, !!entry.migration)}); + if (entry.migration) { + const file = prefix + "src/migrate.ts"; + const before = await fs.readFile(path.join(root, file), "utf8"); + files.push({file, expected: before, replace: addImplementation(before, "serveMigration", entry.id, `./${entry.file.slice(4, -3)}.js`)}); + } + } else { + create("src/server.ts", `import {servePackageRuntime} from "@quixos/camino-package-runtime";\nimport {createRuntime} from "./gen/qx.js";\n` + packageModel.exports.filter((entry) => !entry.migration).map((entry, index) => `import {handler as impl${index}} from ${JSON.stringify(`./${entry.file.slice(4, -3)}.js`)};\n`).join("") + + `servePackageRuntime(createRuntime({\n` + packageModel.exports.map((entry) => ` ${JSON.stringify(entry.name)}: ${entry.migration ? 'async () => { throw new Error("Migration-only export"); }' : `impl${packageModel.exports.filter((value) => !value.migration).indexOf(entry)}`},`).join("\n") + `\n}));\n`); + const migrations = packageModel.exports.filter((entry) => entry.migration); + create("src/migrate.ts", `import {serveMigration} from "@quixos/camino-package-runtime";\n` + migrations.map((entry, index) => `import {handler as impl${index}} from ${JSON.stringify(`./${entry.file.slice(4, -3)}.js`)};\n`).join("") + `await serveMigration({${migrations.map((entry, index) => `${JSON.stringify(entry.id)}: impl${index}`).join(", ")}});\n`); + } + generated("descriptor.quixos-package.txtpb", `# Generated by qx-scaffold-v1\npackage_id: ${JSON.stringify(packageModel.id)}\npackage_revision_id: ${JSON.stringify(packageModel.revision)}\nruntime_protocol_version: "quixos-capabilities-v1"\n` + packageModel.exports.map((entry) => `exports: { export_id: ${JSON.stringify(entry.id)} runtime_symbol: ${JSON.stringify(entry.name)} }\n`).join("")); + return {kind: "package", source: spec.source, resourceRoot: spec.directory, validation: "syntax", files}; }; diff --git a/src/capability-language/structural-plan.ts b/src/capability-language/structural-plan.ts index c81f101..2e8fbc8 100644 --- a/src/capability-language/structural-plan.ts +++ b/src/capability-language/structural-plan.ts @@ -17,7 +17,7 @@ export type StructuralRequest = { source?: {repository: string; commit: string}; resourceRoot?: string; validation?: "syntax" | "resource-graph"; - files: ({file: string; edits: StructuralEdit[]} | {file: string; create: string} | {file: string; generated: string})[]; + files: ({file: string; edits: StructuralEdit[]} | {file: string; create: string} | {file: string; generated: string} | {file: string; expected: string; replace: string})[]; }; type Change = {file: string; before: string | null; after: string; mode: number}; type Journal = {schemaVersion: 1; id: string; root: string; phase: "prepared" | "complete"; changes: Change[]}; @@ -70,6 +70,9 @@ export const planStructure = async (rootPath: string, request: StructuralRequest if ("create" in input) { if (before !== null || typeof input.create !== "string") throw new Error("Scaffold creation cannot replace an existing file"); after = input.create; + } else if ("replace" in input) { + if (before !== input.expected || typeof input.replace !== "string") throw new Error(`Stale imperative edit: ${input.file}`); + after = input.replace; } else if ("generated" in input) { const generated = (text: string) => text.startsWith("// Generated by qx-scaffold-v1\n") || text.startsWith("# Generated by qx-scaffold-v1\n") || (() => {try {return JSON.parse(text).generatedBy === "qx-scaffold-v1";} catch {return false;}})(); if (typeof input.generated !== "string" || !generated(input.generated) || (before !== null && !generated(before))) throw new Error("Only scaffold-owned generated files may be regenerated"); @@ -79,17 +82,6 @@ export const planStructure = async (rootPath: string, request: StructuralRequest after = input.edits.reduce(editStructure, before); } if (Buffer.byteLength(after) > 1024 * 1024) throw new Error("Scaffold file exceeds 1 MiB"); - if (input.file.endsWith("package.qx")) { - let registry; - try { registry = JSON.parse(await fs.readFile(path.join(root, path.dirname(input.file), "quixos.scaffold.json"), "utf8")); } - catch (error) { if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; } - if (registry?.generatedBy === "qx-scaffold-v1") { - const declaration = parseQx(after).root.children.find(node => node.kind === "packageResourceDecl"); - const literal = declaration?.children.find(node => node.kind === "stringLiteral"); - if (literal && JSON.parse(after.slice(literal.start, literal.end)) !== registry.id) - throw new Error("Cannot change a scaffold-owned package identity independently of its registry; create a new managed package instead"); - } - } const mode = before === null ? 0o644 : (await fs.stat(path.join(root, input.file))).mode & 0o777; changes.push({file: input.file, before, after, mode}); await containedParent(snapshot.directory, input.file); @@ -109,9 +101,7 @@ export const planStructure = async (rootPath: string, request: StructuralRequest if (request.kind === "workspace") await compileWorkspaceRepository({rootDirectory: resourceRoot, resolveResource}); else if (["package", "interface"].includes(request.kind) && request.source) { const compiled = await compileCapabilityResourceRepository({rootDirectory: resourceRoot, kind: request.kind as "package" | "interface", source: {resolver: "git", ...request.source}, resolveResource}); - let scaffoldOwned = false; - try { scaffoldOwned = JSON.parse(await fs.readFile(path.join(resourceRoot, "quixos.scaffold.json"), "utf8")).generatedBy === "qx-scaffold-v1"; } catch { /* ordinary resource, no generated package scaffolding */ } - if (scaffoldOwned && compiled.resource.kind === "package") { + if (compiled.resource.kind === "package") { const configuration = JSON.parse(await fs.readFile(path.join(resourceRoot, "quixos.check.json"), "utf8")); const artifacts = [ {file: configuration.bindingOutput as string, after: generateTypeScriptBindings(bindingSchema(compiled), compiled.resource.revision.revisionId, configuration.options)}, diff --git a/src/capability-language/tool-cli.ts b/src/capability-language/tool-cli.ts index 4449bb0..e96fc20 100644 --- a/src/capability-language/tool-cli.ts +++ b/src/capability-language/tool-cli.ts @@ -11,6 +11,9 @@ import os from "node:os"; import {spawnSync} from "node:child_process"; import { planStructure, applyStructure, resumeStructure, type StructuralRequest } from "./structural-plan.js"; import {scaffoldRecipe, type ScaffoldRecipe} from "./scaffold-recipes.js"; +import {checkBundleSources} from "../bindings/bundle-policy.js"; +import {sealMigrations} from "./migration-seal.js"; +import {generatePackageDescriptor} from "../bindings/index.js"; import {buildCheckedPackage, buildImmutableCandidate, snapshotCommit} from "./checked-build.js"; import {formatQuixosLock, loadQuixosLock, parseQuixosLockDocument} from "../resource-lock/index.js"; import { walkSyntax } from "./source.js"; @@ -38,6 +41,32 @@ const planSummary = (plan: Awaited>) => ({ const main = async () => { const [command, ...args] = process.argv.slice(2); + if (command === "package-identity") { + if (args.length !== 1) throw new Error("usage: quixos-qx package-identity PACKAGE_QX"); + const authored = await readFile(args[0], "utf8"); + const syntax = parseQx(authored); + const declarations = [...walkSyntax(syntax.root)].filter(node => node.kind === "packageResourceDecl"); + if (syntax.diagnostics.length || declarations.length !== 1) throw new Error("Expected one valid package declaration"); + const literals = declarations[0].children.filter(node => node.kind === "stringLiteral"); + process.stdout.write(JSON.stringify({id: JSON.parse(authored.slice(literals[0].start, literals[0].end)), + revision: JSON.parse(authored.slice(literals[1].start, literals[1].end))}) + "\n"); + return; + } + if (command === "package-descriptor") { + if (args.length !== 2) throw new Error("usage: quixos-qx package-descriptor SCHEMA REVISION_ID"); + process.stdout.write(generatePackageDescriptor(JSON.parse(await readFile(args[0], "utf8")), args[1])); + return; + } + if (command === "migration-seal") { + if (args.length !== 1) throw new Error("usage: quixos-qx migration-seal PACKAGE_DIRECTORY"); + process.stdout.write(JSON.stringify(await sealMigrations(args[0])) + "\n"); + return; + } + if (command === "bundle-policy") { + if (args.length !== 1) throw new Error("usage: quixos-qx bundle-policy SOURCE_DIRECTORY"); + await checkBundleSources(args[0]); + return; + } if (command === "worklist" && !args.includes("--help")) { if (args.length !== 1) throw new Error("usage: quixos-qx worklist WORKBENCH"); process.stdout.write(`${JSON.stringify(await authoringWorklist(args[0]), null, 2)}\n`); @@ -62,10 +91,10 @@ const main = async () => { if (!args.length || args.length > 2) throw new Error("usage: quixos-qx converge WORKBENCH [REGISTERED_DIRECTORY] (join package writers first)"); const context = await authoringContext(args[0]); if (command === "converge") { - const result = spawnSync("flock", ["--exclusive", "--nonblock", "--conflict-exit-code", "75", path.join(context.workbench, ".quixos/converge.lock"), + const result = spawnSync("flock", ["--exclusive", "--timeout", "120", "--conflict-exit-code", "75", path.join(context.workbench, ".quixos/converge.lock"), process.execPath, process.argv[1], "_converge", context.workbench, ...(args[1] ? [args[1]] : [])], {stdio: "inherit"}); if (result.error) throw result.error; - if (result.status === 75) process.stderr.write("Another source coordinator is running; retry when it finishes.\n"); + if (result.status === 75) process.stderr.write("Timed out after 120 seconds waiting for source capture; inspect the active coordinator. No build lock is held.\n"); process.exitCode = result.status ?? 1; return; } const result = await convergeAuthoring(context.workbench, args[1]); @@ -130,7 +159,7 @@ const main = async () => { const parsed = parseQuixosLockDocument(await readFile(path.join(root, file), "utf8")); if (parsed.ok && parsed.document.resources.some(entry => entry.kind === kind && entry.binding === name)) target = file; } - const request: StructuralRequest = {kind: entrypoint, source: await authorSource(root), files: [ + const request: StructuralRequest = {kind: entrypoint, source: await authorSource(root), validation: "syntax", files: [ {file: `${entrypoint}.qx`, edits: [{operation: "import", kind: resourceKind, name}]}, {file: target, edits: [{operation: "dependency", kind: resourceKind, name, source: {repository, commit}}]}, ]}; @@ -180,12 +209,17 @@ const main = async () => { process.stdout.write(`${JSON.stringify(publish ? await applyPinUpgrades(plan, resume, undefined, {acceptEdits}) : plan, null, 2)}\n`); return; } - if (["scaffold-package", "scaffold-function", "scaffold-migration", "scaffold-refresh"].includes(command)) { + if (command === "scaffold-refresh") throw new Error("Refresh is no longer required: edit declarations and typed implementation wiring, then run qx-workspace check. Dependency installation uses scaffold install."); + if (["scaffold-package", "scaffold-function", "scaffold-migration", "scaffold-install"].includes(command)) { const [root, specFile, ...flags] = args; let spec: ScaffoldRecipe; if (command === "scaffold-function" && /^[A-Za-z_][A-Za-z0-9_]*$/.test(specFile ?? "")) { - const registry = JSON.parse(await readFile(path.join(root, "quixos.scaffold.json"), "utf8")); - spec = {source: await authorSource(root), name: specFile, id: `export:${registry.name}:${specFile}`}; + const authored = parseQx(await readFile(path.join(root, "package.qx"), "utf8")); + const declarationNode = [...walkSyntax(authored.root)].find(node => node.kind === "packageResourceDecl"); + const nameNode = declarationNode?.children.find(node => node.kind === "identifier"); + if (!nameNode) throw new Error("Expected a package declaration"); + const name = authored.source.slice(nameNode.start, nameNode.end); + spec = {source: await authorSource(root), name: specFile, id: `export:${name}:${specFile}`}; const declaration = flags.indexOf("--declaration"); if (declaration >= 0) { if (!flags[declaration + 1]) throw new Error("--declaration requires a QX declaration file"); @@ -201,9 +235,9 @@ const main = async () => { } } else spec = await readSpec(specFile) as ScaffoldRecipe; if (!root || !specFile || flags.some((flag) => !["--write", "--install"].includes(flag)) || (flags.includes("--install") && !flags.includes("--write"))) throw new Error("usage: quixos-qx scaffold-package|function|migration|refresh ROOT SPEC_JSON [--write [--install]]"); - const request = await scaffoldRecipe(root, command.slice(9) as "package" | "function" | "migration" | "refresh", spec); - const plan = await planStructure(root, request, process.env.QUIXOS_SNAPSHOT_MAP); - const applied = flags.includes("--write") ? await applyStructure(plan) : undefined; + const plan = command === "scaffold-install" ? undefined : await planStructure(root, + await scaffoldRecipe(root, command.slice(9) as "package" | "function" | "migration", spec), process.env.QUIXOS_SNAPSHOT_MAP); + const applied = plan && flags.includes("--write") ? await applyStructure(plan) : undefined; if (flags.includes("--install")) { const cwd = path.resolve(root, spec.directory ?? ""); const toolchain = JSON.parse(await readFile(path.join(cwd, "quixos.toolchain.json"), "utf8")); @@ -218,7 +252,7 @@ const main = async () => { const locked = spawnSync("nix", ["flake", "lock"], {cwd, stdio: ["inherit", 2, 2]}); if (locked.error || locked.status !== 0) throw new Error("Scaffold files retained; nix flake lock failed"); } - process.stdout.write(`${JSON.stringify({...planSummary(plan), applied}, null, 2)}\n`); + process.stdout.write(`${JSON.stringify({...plan ? planSummary(plan) : {installed: true}, applied}, null, 2)}\n`); return; } if (command === "scaffold-structure") { diff --git a/test/bundle-policy.test.ts b/test/bundle-policy.test.ts new file mode 100644 index 0000000..b328036 --- /dev/null +++ b/test/bundle-policy.test.ts @@ -0,0 +1,21 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import {bundlePolicyErrors} from "../src/bindings/bundle-policy.js"; +import {addImplementation} from "../src/capability-language/implementation-edit.js"; + +test("bundled source policy rejects location and dynamic-loading assumptions, not static assets or runtime I/O", () => { + for (const code of ['new URL("../x", import.meta.url)', '__dirname', '__filename', 'import(name)', 'require(name)', 'eval(code)', 'new Function(code)']) + assert.ok(bundlePolicyErrors(code, "source.ts").length, code); + assert.deepEqual(bundlePolicyErrors('import source from "./component.js?browser-source"; import fs from "node:fs"; fs.readFile(userSelectedPath);', "source.ts"), []); + assert.deepEqual(bundlePolicyErrors('// import.meta.url\nconst text = "__dirname";', "source.ts"), []); +}); + +test("imperative handler insertion preserves arbitrary existing code and rejects ambiguous targets", () => { + const original = 'const keep = "createRuntime({fake:1})";\nservePackageRuntime(createRuntime({ existing: customHandler }));\n'; + const edited = addImplementation(original, "createRuntime", "newHandler", "./impl/new.js"); + assert.match(edited, /existing: customHandler/); + assert.match(edited, /const keep =/); + assert.match(edited, /"newHandler": qxImplementation/); + assert.throws(() => addImplementation(original, "createRuntime", "existing", "./x.js"), /already exists/); + assert.throws(() => addImplementation('createRuntime(one);', "createRuntime", "x", "./x.js"), /Cannot safely/); +}); diff --git a/test/scaffold-recipes.test.ts b/test/scaffold-recipes.test.ts index 5f3e68a..9ffc174 100644 --- a/test/scaffold-recipes.test.ts +++ b/test/scaffold-recipes.test.ts @@ -8,6 +8,7 @@ import {promisify} from "node:util"; import {scaffoldRecipe} from "../src/capability-language/scaffold-recipes.js"; import {planStructure, applyStructure} from "../src/capability-language/structural-plan.js"; import {contentDigest} from "../src/capability-model/evolution.js"; +import {sealMigrations} from "../src/capability-language/migration-seal.js"; const execFile = promisify(callback); test("React preset applies its browser build script and shared-platform imports", async (context) => { @@ -17,11 +18,14 @@ test("React preset applies its browser build script and shared-platform imports" const source = {repository: "https://example.test/react.git", commit: "a".repeat(40)}; const request = await scaffoldRecipe(root, "package", {source, name: "React", id: "package:react", revision: "package:react@1", template: "typescript-react", tools: {quixos: source, protocol: source, helpers: source, sdk: source}}); await applyStructure(await planStructure(root, request)); - assert.match(await fs.readFile(path.join(root, "scripts/build-component.mjs"), "utf8"), /__quixos\/platform\/react/); + assert.match(await fs.readFile(path.join(root, "src/impl/sourceGet.ts"), "utf8"), /component.js\?browser-source/); + assert.match(await fs.readFile(path.join(root, "flake.nix"), "utf8"), /browserSources = true/); + assert.equal(JSON.parse(await fs.readFile(path.join(root, "quixos.check.json"), "utf8")).options.messages["org.quixos.web-studio.ReactProps"].export, "opaqueReactPropsBinding"); + await assert.rejects(fs.access(path.join(root, "quixos.scaffold.json"))); assert.equal(JSON.parse(await fs.readFile(path.join(root, "tsconfig.json"), "utf8")).compilerOptions.jsx, "react-jsx"); }); -test("package/function/migration scaffolds register implementations and refresh code digests without overwriting code", async (context) => { +test("imperative scaffolds preserve authored wiring; migration sealing is explicit and separate", async (context) => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "qx-recipes-")); context.after(() => fs.rm(root, {recursive: true, force: true})); await execFile("git", ["-C", root, "init"]); @@ -38,31 +42,23 @@ test("package/function/migration scaffolds register implementations and refresh await apply("migration", {...base, name: "upgrade", id: "export:upgrade", migration: {id: "upgrade-v2", scopeId: "board", from, to, predecessors: [], ports: [], contracts: {[from]: old, [to]: next}}}); const migrationFile = path.join(root, base.directory, "src/migrations/upgrade.ts"); await fs.appendFile(migrationFile, "\n// authored migration change\n"); - await apply("refresh", base); + await sealMigrations(path.join(root, base.directory)); assert.equal(await fs.readFile(filename, "utf8"), edited); const catalog = JSON.parse(await fs.readFile(path.join(root, base.directory, "quixos.migrations.json"), "utf8")); assert.equal(catalog.migrations[0].implementation.digest, contentDigest(await fs.readFile(migrationFile, "utf8"))); - const bindings = await fs.readFile(path.join(root, base.directory, "src/gen/qx.ts"), "utf8"); - assert.match(bindings, /export:play/); assert.match(await fs.readFile(path.join(root, base.directory, "src/migrate.ts"), "utf8"), /export:upgrade/); - if (process.env.QX_SCAFFOLD_TEST_SDK) { - const sdk = await fs.realpath(process.env.QX_SCAFFOLD_TEST_SDK); - const packageRoot = path.join(root, base.directory); - await fs.mkdir(path.join(packageRoot, "node_modules/@quixos"), {recursive: true}); - await fs.symlink(sdk, path.join(packageRoot, "node_modules/@quixos/camino-package-runtime")); - await fs.symlink(path.join(sdk, "node_modules/@types"), path.join(packageRoot, "node_modules/@types")); - await execFile(path.join(sdk, "node_modules/.bin/tsc"), ["--noEmit"], {cwd: packageRoot}); - await execFile("nix-instantiate", ["--parse", path.join(packageRoot, "flake.nix")]); - } await assert.rejects(() => apply("function", {...base, name: "play", id: "export:play"}), /unique/); const declarations = path.join(root, base.directory, "package.qx"); await fs.writeFile(declarations, (await fs.readFile(declarations, "utf8")).replace(/}\s*$/, ' function authored id "export:authored" : unit -> unit;\n}\n')); - await apply("refresh", base); - assert.match(await fs.readFile(path.join(root, base.directory, "src/server.ts"), "utf8"), /"authored":/); - assert.match(await fs.readFile(path.join(root, base.directory, "src/impl/authored.ts"), "utf8"), /Implement authored/); + const server = path.join(root, base.directory, "src/server.ts"); + await fs.appendFile(server, "\n// authored comment must survive\n"); + await apply("function", {...base, name: "another", id: "export:another"}); + assert.match(await fs.readFile(server, "utf8"), /authored comment must survive/); + await assert.rejects(() => apply("refresh", base), /removed/); + await assert.rejects(fs.access(path.join(root, base.directory, "src/impl/authored.ts"))); assert.equal(await fs.readFile(filename, "utf8"), edited); - await assert.rejects(() => planStructure(root, {kind: "package", source, resourceRoot: base.directory, validation: "syntax", files: [{ + await planStructure(root, {kind: "package", source, resourceRoot: base.directory, validation: "syntax", files: [{ file: `${base.directory}/package.qx`, edits: [{operation: "replace", target: {kind: "packageResourceDecl", id: "package:chess"}, source: 'package Other id "package:other" revision "package:other@1" {}'}], - }]}), /scaffold-owned package identity/); + }]}); }); diff --git a/yarn-project.nix b/yarn-project.nix index 32b128f..b773583 100644 --- a/yarn-project.nix +++ b/yarn-project.nix @@ -156,6 +156,10 @@ let overriddenProject = optionalOverride overrideAttrs project; cacheEntries = { +"@babel/helper-string-parser@npm:7.29.7" = { filename = "@babel-helper-string-parser-npm-7.29.7-87998d618e-194bc0f171.zip"; hash = "sha512-GUvA8XFuOW1f/eVq1hGXRfuVV2YsmGEVkOXkVJBng6TMshzpMFa462mkkJBEg05F2W5QrGlbvp4yIWSP4DPAbA=="; }; +"@babel/helper-validator-identifier@npm:7.29.7" = { filename = "@babel-helper-validator-identifier-npm-7.29.7-9939aac13d-4795354e7a.zip"; hash = "sha512-R5U1Tnrg3K+nLeHNBOxRJS3BSYUXFwvq8BngPv/Ft78TxrIaOUmnfge4Elvn8QbtETE1DY69RWauh0CUpybWKw=="; }; +"@babel/parser@npm:7.29.8" = { filename = "@babel-parser-npm-7.29.8-d8ac19f8b3-acc890c5e6.zip"; hash = "sha512-rMiQxeam3UCGOke1C6wRHXGF7m+74WPr4R1SFIVMoq25AUYq1NcYplCQ74S9IjDp6KtFouDKzMaF8fV6sLseKA=="; }; +"@babel/types@npm:7.29.8" = { filename = "@babel-types-npm-7.29.8-3f9597fc63-be7c279f0a.zip"; hash = "sha512-vnwnnwq/KghsYz4htJx8qAJ10FKDzFomi2enCMmRS9DJRPFCKz6zyzdoKir11WCr9SDM+bAbU+y/5rcfvD/d5g=="; }; "@bufbuild/protobuf@npm:2.14.1" = { filename = "@bufbuild-protobuf-npm-2.14.1-78e9ea56a2-3f913aca03.zip"; hash = "sha512-P5E6ygPfhBnEsh2ADqVNf2yDalND5l1vaGJ57DVmxJB3KZOO8vlKXifZLo0/StzW4CcRi6Kbp2TSHhxjfN3HAg=="; }; "@bufbuild/protoc-gen-es@npm:2.14.1" = { filename = "@bufbuild-protoc-gen-es-npm-2.14.1-230b1181a2-79f9fad1d5.zip"; hash = "sha512-efn60dVmXaptZUmyh4oPY/6/rbGHuFNAg/vrI4Ydwizz6mmmYKEvD45Ps8AtJO6ALxcYRBVt5C+0sXEuOgZ1aQ=="; }; "@bufbuild/protoplugin@npm:2.14.1" = { filename = "@bufbuild-protoplugin-npm-2.14.1-ca1a20a987-6a727aa5a8.zip"; hash = "sha512-anJ6pahI4FA13tkPUOE65Sx3NXutalhHalbU/3ANyBPKxmTgzDC1IBNU5cFY6//rANyDUxbWaqLDhKLp2qXf4w=="; }; diff --git a/yarn.lock b/yarn.lock index 2c3508a..1ee94ec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,6 +5,41 @@ __metadata: version: 10 cacheKey: 10c0 +"@babel/helper-string-parser@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-string-parser@npm:7.29.7" + checksum: 10c0/194bc0f1716e396d5ffde56ad6119745fb9557662c98611590e5e454906783a4ccb21ce93056b8eb69a4909044834e45d96e50ac695bbe9e3221648fe033c06c + languageName: node + linkType: hard + +"@babel/helper-validator-identifier@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-validator-identifier@npm:7.29.7" + checksum: 10c0/4795354e7ae0dcafa72de1cd04ec51252dc1498517170beaf019e03effc5b7bf13c6b21a3949a77e07b8125be7f106ed1131350d8ebd4566ae874094a726d62b + languageName: node + linkType: hard + +"@babel/parser@npm:^7.28.0": + version: 7.29.8 + resolution: "@babel/parser@npm:7.29.8" + dependencies: + "@babel/types": "npm:^7.29.8" + bin: + parser: ./bin/babel-parser.js + checksum: 10c0/acc890c5e6a6dd40863a47b50bac111d7185ee6fbbe163ebe11d5214854ca2adb901462ad4d718a65090ef84bd2230e9e8ab45a2e0caccc685f1f57ab0bb1e28 + languageName: node + linkType: hard + +"@babel/types@npm:^7.29.8": + version: 7.29.8 + resolution: "@babel/types@npm:7.29.8" + dependencies: + "@babel/helper-string-parser": "npm:^7.29.7" + "@babel/helper-validator-identifier": "npm:^7.29.7" + checksum: 10c0/be7c279f0abf2a086c633e21b49c7ca80275d05283cc5a268b67a708c9914bd0c944f1422b3eb3cb37682a2af5d560abf520ccf9b01b53ecbfe6b71fbc3fdde6 + languageName: node + linkType: hard + "@bufbuild/protobuf@npm:2.14.1, @bufbuild/protobuf@npm:^2.12.1": version: 2.14.1 resolution: "@bufbuild/protobuf@npm:2.14.1" @@ -44,6 +79,7 @@ __metadata: version: 0.0.0-use.local resolution: "@quixos/quixos-protocol@workspace:." dependencies: + "@babel/parser": "npm:^7.28.0" "@bufbuild/protobuf": "npm:^2.12.1" "@bufbuild/protoc-gen-es": "npm:^2.12.1" "@types/node": "npm:^24" From a84c98525671df07519e44cfa4086afdb0f51ec1 Mon Sep 17 00:00:00 2001 From: "Timothy J. Aveni" Date: Mon, 14 Sep 2026 22:11:36 -0700 Subject: [PATCH 02/11] Fix standalone candidate binding options and test real React props contracts --- nix/checked-candidate.nix | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nix/checked-candidate.nix b/nix/checked-candidate.nix index fe8b396..8c55161 100644 --- a/nix/checked-candidate.nix +++ b/nix/checked-candidate.nix @@ -63,6 +63,8 @@ let in { packageRevisionId = candidate.revision.revisionId; artifactPath = artifact; }; checks = if contractOnly then [ ] else map checkPackage (builtins.filter (node: node.kind == "package") nodes); manifest = pkgs.writeText "qx-candidate-checks.json" (builtins.toJSON checks); + bindingOptions = pkgs.writeText "qx-typescript-options.json" (builtins.toJSON + ((builtins.fromJSON (builtins.readFile "${root.directory}/quixos.check.json")).options or { })); in pkgs.runCommand (if contractOnly then "qx-contract" else "qx-checked-candidate") { } '' mkdir -p "$out" cp ${contract}/candidate.json "$out/candidate.json" @@ -72,6 +74,6 @@ in pkgs.runCommand (if contractOnly then "qx-contract" else "qx-checked-candidat ${pkgs.lib.optionalString (kind == "package") '' ${protocol}/bin/quixos-codegen-ts ${contract}/bindings.json \ ${pkgs.lib.escapeShellArg (builtins.fromJSON (builtins.readFile "${contract}/candidate.json")).revision.revisionId} \ - "$out/bindings.ts" ${pkgs.lib.optionalString (builtins.pathExists (root.directory + "/bindings.json")) (toString root.directory + "/bindings.json")} + "$out/bindings.ts" ${bindingOptions} ''} '' From 53708ac06f00cfb590dea9b91d9540f0fb80f62a Mon Sep 17 00:00:00 2001 From: "Timothy J. Aveni" Date: Mon, 14 Sep 2026 23:38:07 -0700 Subject: [PATCH 03/11] Provide typed React live-field authoring, strict CLI arguments, and timed build stages --- flake.nix | 2 + src/bindings/cli.ts | 9 ++- src/bindings/react-platform.ts | 62 +++++++++++++++++++++ src/capability-language/authoring-check.ts | 11 +++- src/capability-language/scaffold-recipes.ts | 2 + test/scaffold-recipes.test.ts | 2 + 6 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 src/bindings/react-platform.ts diff --git a/flake.nix b/flake.nix index 54ad388..0477270 100644 --- a/flake.nix +++ b/flake.nix @@ -52,6 +52,7 @@ --bundle --platform=node --target=node24 --format=esm \ --outfile=quixos-lock-check.mjs esbuild dist/src/bindings/cli.js --bundle --platform=node --target=node24 --format=esm --outfile=quixos-codegen-ts.mjs + esbuild dist/src/bindings/react-platform.js --bundle --platform=node --target=node24 --format=esm --outfile=react-platform.mjs esbuild dist/src/capability-language/tool-cli.js --bundle --platform=node --target=node24 --format=esm --outfile=quixos-qx.mjs runHook postBuild ''; @@ -105,6 +106,7 @@ EOF chmod +x "$out/bin/quixos-lock-check" mkdir -p "$out/libexec/quixos-protocol" install -m644 client-codegen.mjs "$out/libexec/quixos-protocol/client-codegen.mjs" + install -m644 react-platform.mjs "$out/libexec/quixos-protocol/react-platform.mjs" install -m644 quixos-codegen-ts.mjs quixos-qx.mjs "$out/libexec/quixos-protocol/" install -m644 quixos-descriptor-check.mjs "$out/libexec/quixos-protocol/quixos-descriptor-check.mjs" install -m644 quixos-capability-compile.mjs "$out/libexec/quixos-protocol/quixos-capability-compile.mjs" diff --git a/src/bindings/cli.ts b/src/bindings/cli.ts index f3a0c01..21f34fc 100644 --- a/src/bindings/cli.ts +++ b/src/bindings/cli.ts @@ -1,13 +1,18 @@ #!/usr/bin/env node import { readFile, writeFile } from "node:fs/promises"; import { generateTypeScriptBindings } from "./index.js"; +import path from "node:path"; +import {reactPlatformTypes} from "./react-platform.js"; const main = async () => { const [schema, revision, output, options, ...rest] = process.argv.slice(2); if (!schema || !revision || !output || rest.length) throw new Error( "usage: quixos-codegen-ts SCHEMA.json PACKAGE_REVISION OUTPUT.ts [OPTIONS.json]"); - const generated = generateTypeScriptBindings(JSON.parse(await readFile(schema, "utf8")), revision, - options ? JSON.parse(await readFile(options, "utf8")) : {}); + const config = options ? JSON.parse(await readFile(options, "utf8")) : {}; + const generated = generateTypeScriptBindings(JSON.parse(await readFile(schema, "utf8")), revision, config); await writeFile(output, generated); + if (config.messages?.["org.quixos.web-studio.ReactProps"]) { + await writeFile(path.join(path.dirname(output), "web-studio-react-runtime.d.ts"), reactPlatformTypes); + } }; main().catch((error: unknown) => { console.error(error instanceof Error ? error.message : error); process.exitCode = 1; }); diff --git a/src/bindings/react-platform.ts b/src/bindings/react-platform.ts new file mode 100644 index 0000000..0d3a997 --- /dev/null +++ b/src/bindings/react-platform.ts @@ -0,0 +1,62 @@ +/** One authoring contract, distributed by scaffolding and immutable codegen. + * Web Studio checks its concrete exports against this contract during its build. */ +export const reactPlatformTypes = `// Generated by the Quixos React platform contract. Do not edit. +declare module "@quixos/web-studio-react-runtime" { + import type * as React from "react"; + export function useComponentOverlayContainer(): HTMLElement; + export function useComponentStyleRoot(): ShadowRoot; + export type ObjectRef = string & { + readonly $quixosAtom: AtomId; + }; + export type LiveFieldProp = { + value: T; + source: { + objectId: string; + slotId: string; + valueType?: string; + storagePolicy?: string; + revision?: string | number | bigint; + crdtSnapshot?: { + type: string; + encoding: string; + payload: string; + }; + }; + }; + export type ReactComponentHostProps = { + onAction?: (action: Action) => void; + fallback?: React.ReactNode; + className?: string; + style?: React.CSSProperties; + onError?: (error: Error) => void; + }; + export type ReactComponentImplementationProps = { + camino: CaminoProps; + render: RenderProps; + dispatch: (action: Action) => void; + }; + export const createWebStudioComponent: < + ForObject extends ObjectRef, RenderProps extends object, Action, + >(config: { expectedAtomId: string }) => (props: { + forObject: ForObject; + } & RenderProps & ReactComponentHostProps) => any; + export const invokeCapability: ( + objectId: string, + interfaceRevisionId: string, + operationId: string, + value?: unknown, + options?: {clientMutationId?: string}, + ) => Promise; + export const h: typeof React.createElement; + export const useLiveField: ( + field: LiveFieldProp, + options?: { + reconcileRegister?: (state: { + confirmed: T; + optimistic: T; + pending: boolean; + }) => T; + }, + ) => readonly [T, (value: T) => Promise]; +} +`; diff --git a/src/capability-language/authoring-check.ts b/src/capability-language/authoring-check.ts index 656bab8..3dae757 100644 --- a/src/capability-language/authoring-check.ts +++ b/src/capability-language/authoring-check.ts @@ -22,6 +22,7 @@ export async function checkAuthoring(start: string, output: string, options: { b directory, checker: checkerIdentity(), candidateOnly: true, activationEvidence: false, blockers: [], phase: "convergence", output, }; try { + console.error(`[${new Date().toISOString()}] Check: capture source and converge dependencies`); // Serialize only source capture, not the potentially slow Nix build. // Repository-scoped agents can check separate immutable candidates in parallel. const captured = await promisify(callback)("quixos-qx", ["converge", context.workbench, directory], { @@ -32,6 +33,7 @@ export async function checkAuthoring(start: string, output: string, options: { b }); const converged = JSON.parse(captured.stdout) as Awaited>; timings.captureMs = Math.round(performance.now() - started); + console.error(`[${new Date().toISOString()}] Check: source captured in ${(timings.captureMs / 1000).toFixed(1)}s`); if (!converged.candidate) { report.phase = converged.worklist.find(entry => entry.phase !== "dependency")?.phase ?? "convergence"; throw new Error(converged.worklist.map(entry => `${entry.directory} [${entry.phase}]: ${entry.message}`).join("\n")); @@ -39,8 +41,13 @@ export async function checkAuthoring(start: string, output: string, options: { b report.commit = converged.candidate.commit; report.phase = "verification"; const buildStarted = performance.now(); - report.artifactPath = await buildImmutableCandidate(converged.candidate, resource.kind, path.join(output, "nix.log"), options.contractOnly); - timings.immutableCheckMs = Math.round(performance.now() - buildStarted); + console.error(`[${new Date().toISOString()}] Check: immutable Nix ${options.contractOnly ? "contract" : "verification"}; build output: ${path.join(output, "nix.log")}`); + try { + report.artifactPath = await buildImmutableCandidate(converged.candidate, resource.kind, path.join(output, "nix.log"), options.contractOnly); + } finally { + timings.immutableCheckMs = Math.round(performance.now() - buildStarted); + console.error(`[${new Date().toISOString()}] Check: immutable phase ended after ${(timings.immutableCheckMs / 1000).toFixed(1)}s`); + } const candidateText = await fs.readFile(path.join(report.artifactPath, "candidate.json"), "utf8"); await fs.writeFile(path.join(output, "candidate.json"), candidateText); report.compilation = "passed"; diff --git a/src/capability-language/scaffold-recipes.ts b/src/capability-language/scaffold-recipes.ts index 057f3d3..8f8e0e9 100644 --- a/src/capability-language/scaffold-recipes.ts +++ b/src/capability-language/scaffold-recipes.ts @@ -6,6 +6,7 @@ import {formatQuixosLock, type GitSource} from "../resource-lock/index.js"; import {parseQx, walkSyntax} from "./source.js"; import type {StructuralRequest} from "./structural-plan.js"; import {addImplementation} from "./implementation-edit.js"; +import {reactPlatformTypes} from "../bindings/react-platform.js"; type Source = {repository: string; commit: string}; type PackageEditModel = {generatedBy: "qx-scaffold-v1"; name: string; id: string; revision: string; exports: {name: string; id: string; file: string; migration?: boolean}[]}; @@ -69,6 +70,7 @@ export const scaffoldRecipe = async (root: string, command: "package" | "functio create("src/component.tsx", `// Props are opaque at the platform boundary until capability generics exist.\nexport default function Component(_props: {camino: unknown; render: unknown; dispatch: (action: unknown) => void}) {\n return

${name}

Edit this component, then run qx-workspace check.

;\n}\n`); create("src/impl/sourceGet.ts", `import componentSource from "../component.js?browser-source";\nimport type {Implementation} from "../gen/qx.js";\nexport const handler: Implementation["sourceGet"] = () => componentSource;\n`); create("src/browser-assets.d.ts", `declare module "*?browser-source" { const source: string; export default source; }\n`); + create("src/gen/web-studio-react-runtime.d.ts", reactPlatformTypes); } create(".gitignore", "node_modules/\ndist/\n.quixos/\nresult\n.yarn/install-state.gz\n"); create(".yarnrc.yml", `nodeLinker: node-modules\nenableScripts: true\nnpmMinimalAgeGate: 0\napprovedGitRepositories:\n - ${JSON.stringify(spec.tools.sdk.repository)}\nsupportedArchitectures:\n os: [current, linux]\n cpu: [current, x64, arm64]\n libc: [current, glibc]\n`); diff --git a/test/scaffold-recipes.test.ts b/test/scaffold-recipes.test.ts index 9ffc174..6b9605a 100644 --- a/test/scaffold-recipes.test.ts +++ b/test/scaffold-recipes.test.ts @@ -9,6 +9,7 @@ import {scaffoldRecipe} from "../src/capability-language/scaffold-recipes.js"; import {planStructure, applyStructure} from "../src/capability-language/structural-plan.js"; import {contentDigest} from "../src/capability-model/evolution.js"; import {sealMigrations} from "../src/capability-language/migration-seal.js"; +import {reactPlatformTypes} from "../src/bindings/react-platform.js"; const execFile = promisify(callback); test("React preset applies its browser build script and shared-platform imports", async (context) => { @@ -20,6 +21,7 @@ test("React preset applies its browser build script and shared-platform imports" await applyStructure(await planStructure(root, request)); assert.match(await fs.readFile(path.join(root, "src/impl/sourceGet.ts"), "utf8"), /component.js\?browser-source/); assert.match(await fs.readFile(path.join(root, "flake.nix"), "utf8"), /browserSources = true/); + assert.equal(await fs.readFile(path.join(root, "src/gen/web-studio-react-runtime.d.ts"), "utf8"), reactPlatformTypes); assert.equal(JSON.parse(await fs.readFile(path.join(root, "quixos.check.json"), "utf8")).options.messages["org.quixos.web-studio.ReactProps"].export, "opaqueReactPropsBinding"); await assert.rejects(fs.access(path.join(root, "quixos.scaffold.json"))); assert.equal(JSON.parse(await fs.readFile(path.join(root, "tsconfig.json"), "utf8")).compilerOptions.jsx, "react-jsx"); From a174faea5cb6dd5e1ede23189768a9b13cf624ad Mon Sep 17 00:00:00 2001 From: "Timothy J. Aveni" Date: Tue, 15 Sep 2026 13:45:42 -0700 Subject: [PATCH 04/11] Add imperative feature bundles, intrinsic component sizing, and authoring timing logs Generate state-backed atoms, interfaces, relationships and React/CSS packages in a resumable command that prints the full source diff. Keep scaffold output ordinary editable code. Shorten installed authoring guides while preserving contracts and clarify historical design context. Test generated bundles through immutable Nix verification and cover browser isolation, nested sizing, interruption recovery, and command help. --- README.md | 6 +-- src/capability-language/authoring-check.ts | 20 ++++++++-- src/capability-language/authoring-converge.ts | 17 +++++++-- src/capability-language/scaffold-recipes.ts | 30 ++++++++++++++- src/capability-language/structural-plan.ts | 2 +- src/capability-language/tool-cli.ts | 38 ++++++++++++++++++- test/scaffold-recipes.test.ts | 1 + 7 files changed, 100 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 7ad63eb..c71eedc 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Quixos protocol and capability compiler -This package owns the shared protobuf APIs for Camino, `quixos-orch`, package -runtimes, package descriptors, and generic runtime values. It also owns the v1 -capability authoring language and semantic compiler. +Shared protobuf APIs for Camino, orch, package runtimes/descriptors and values, +plus the capability language/compiler. Commands below are for backend development; +workspace authors use `qx-workspace check` for all verification. ## Capability language diff --git a/src/capability-language/authoring-check.ts b/src/capability-language/authoring-check.ts index 3dae757..023929e 100644 --- a/src/capability-language/authoring-check.ts +++ b/src/capability-language/authoring-check.ts @@ -1,4 +1,5 @@ import fs from "node:fs/promises"; +import {appendFileSync} from "node:fs"; import path from "node:path"; import { createHash, randomUUID } from "node:crypto"; import { authoringContext } from "./authoring-context.js"; @@ -21,13 +22,22 @@ export async function checkAuthoring(start: string, output: string, options: { b const report: { directory: string; checker: string; candidateOnly: true; activationEvidence: false; commit?: string; artifactPath?: string; blockers: string[]; phase: string; output: string; compilation?: "passed"; activationReadiness?: "preserve" | "migration-required" | "blocked"; migrationRequired?: string[] } = { directory, checker: checkerIdentity(), candidateOnly: true, activationEvidence: false, blockers: [], phase: "convergence", output, }; + const progress = async (running = true) => { + const file = path.join(output, "report.json"), temp = `${file}.tmp`; + await fs.writeFile(temp, JSON.stringify({...report, timings, running}, null, 2)); + await fs.rename(temp, file); + }; + await progress(); try { console.error(`[${new Date().toISOString()}] Check: capture source and converge dependencies`); // Serialize only source capture, not the potentially slow Nix build. // Repository-scoped agents can check separate immutable candidates in parallel. - const captured = await promisify(callback)("quixos-qx", ["converge", context.workbench, directory], { - maxBuffer: 4 * 1024 * 1024, env: {...process.env, QUIXOS_JJ_NO_CHECKPOINT: "1"}, - }).catch(error => { + const capture = promisify(callback)("quixos-qx", ["converge", context.workbench, directory], { + maxBuffer: 4 * 1024 * 1024, env: {...process.env, QUIXOS_JJ_NO_CHECKPOINT: "1", QUIXOS_TRACE_CAPTURE: "1"}, + }); + console.error(`Capture details: ${path.join(output, "capture.log")}`); + capture.child.stderr?.on("data", chunk => appendFileSync(path.join(output, "capture.log"), chunk)); + const captured = await capture.catch(error => { if (typeof error.stdout === "string" && error.stdout.trim().startsWith("{")) return {stdout: error.stdout}; throw error; }); @@ -40,6 +50,7 @@ export async function checkAuthoring(start: string, output: string, options: { b } report.commit = converged.candidate.commit; report.phase = "verification"; + await progress(); const buildStarted = performance.now(); console.error(`[${new Date().toISOString()}] Check: immutable Nix ${options.contractOnly ? "contract" : "verification"}; build output: ${path.join(output, "nix.log")}`); try { @@ -53,6 +64,7 @@ export async function checkAuthoring(start: string, output: string, options: { b report.compilation = "passed"; if (resource.kind === "workspace" && !options.contractOnly) { report.phase = "evolution"; + await progress(); let baseline = options.baseline; if (!baseline) { try { @@ -73,7 +85,7 @@ export async function checkAuthoring(start: string, output: string, options: { b } catch (error) { report.blockers.push(String(error instanceof Error ? error.message : error)); } timings.totalMs = Math.round(performance.now() - started); Object.assign(report, {timings}); - await fs.writeFile(path.join(output, "report.json"), JSON.stringify(report, null, 2)); + await progress(false); if (options.contractOnly) return report; const records = path.join(context.workbench, ".quixos/checks"); await fs.mkdir(records, { recursive: true }); diff --git a/src/capability-language/authoring-converge.ts b/src/capability-language/authoring-converge.ts index a34f7d2..200fd32 100644 --- a/src/capability-language/authoring-converge.ts +++ b/src/capability-language/authoring-converge.ts @@ -8,10 +8,17 @@ import { snapshotCommit } from "./checked-build.js"; import { loadQuixosLock, parseQuixosLockDocument, formatQuixosLockDocument, retentionTagForCommit, type GitSource } from "../resource-lock/index.js"; const execFile = promisify(callback); -const command = async (cwd: string, executable: string, args: string[]) => (await execFile(executable, args, { - cwd, maxBuffer: 4 * 1024 * 1024, - env: { ...process.env, QUIXOS_JJ_NO_CHECKPOINT: "1", QUIXOS_SUBTREE_PUBLISH: "0", GIT_TERMINAL_PROMPT: "0" }, -})).stdout.trim(); +const command = async (cwd: string, executable: string, args: string[]) => { + const start = performance.now(); + // Do not log arguments: transports may contain credentials. Source identities + // remain in the normal checked result, not in this timing channel. + const label = `${path.basename(cwd)} ${executable} ${args[0]}`; + try { return (await execFile(executable, args, { + cwd, maxBuffer: 4 * 1024 * 1024, + env: { ...process.env, QUIXOS_JJ_NO_CHECKPOINT: "1", QUIXOS_SUBTREE_PUBLISH: "0", GIT_TERMINAL_PROMPT: "0" }, + })).stdout.trim(); } + finally { if (process.env.QUIXOS_TRACE_CAPTURE === "1") console.error(`[${new Date().toISOString()}] Capture: ${label}: ${Math.round(performance.now() - start)}ms`); } +}; const identity = (kind: string, repository: string) => `${kind}\0${repository}`; export type AuthoringBlocker = { directory: string; phase: "resolution" | "dependency" | "source" | "publication" | "concurrent-edit"; message: string }; @@ -92,7 +99,9 @@ export async function convergeAuthoring(start: string, target = "root") { } finally { await rm(temporary, { force: true }); } } } + const snapshotStarted = performance.now(); const commit = await snapshotCommit(root); + if (process.env.QUIXOS_TRACE_CAPTURE === "1") console.error(`[${new Date().toISOString()}] Capture: ${directory} snapshot: ${Math.round(performance.now() - snapshotStarted)}ms`); phase = "publication"; const ref = retentionTagForCommit(commit); const remote = await command(root, "git", ["ls-remote", "--refs", node.source.repository, ref]); diff --git a/src/capability-language/scaffold-recipes.ts b/src/capability-language/scaffold-recipes.ts index 8f8e0e9..bbd0f2c 100644 --- a/src/capability-language/scaffold-recipes.ts +++ b/src/capability-language/scaffold-recipes.ts @@ -14,6 +14,8 @@ export type ScaffoldRecipe = { template?: "typescript" | "typescript-react"; source: Source; directory?: string; name?: string; id?: string; revision?: string; declaration?: string; + /** Initial authored files for a composed recipe; only used for a new package. */ + initialFiles?: Record; tools?: {quixos: Source; protocol: Source; helpers: Source; sdk: Source}; nixifyPluginUrl?: string; migration?: Omit & {contracts: Record}; @@ -69,7 +71,7 @@ export const scaffoldRecipe = async (root: string, command: "package" | "functio if (react) { create("src/component.tsx", `// Props are opaque at the platform boundary until capability generics exist.\nexport default function Component(_props: {camino: unknown; render: unknown; dispatch: (action: unknown) => void}) {\n return

${name}

Edit this component, then run qx-workspace check.

;\n}\n`); create("src/impl/sourceGet.ts", `import componentSource from "../component.js?browser-source";\nimport type {Implementation} from "../gen/qx.js";\nexport const handler: Implementation["sourceGet"] = () => componentSource;\n`); - create("src/browser-assets.d.ts", `declare module "*?browser-source" { const source: string; export default source; }\n`); + create("src/browser-assets.d.ts", `declare module "*?browser-source" { const source: string; export default source; }\ndeclare module "*.css" {}\n`); create("src/gen/web-studio-react-runtime.d.ts", reactPlatformTypes); } create(".gitignore", "node_modules/\ndist/\n.quixos/\nresult\n.yarn/install-state.gz\n"); @@ -161,5 +163,31 @@ export const scaffoldRecipe = async (root: string, command: "package" | "functio create("src/migrate.ts", `import {serveMigration} from "@quixos/camino-package-runtime";\n` + migrations.map((entry, index) => `import {handler as impl${index}} from ${JSON.stringify(`./${entry.file.slice(4, -3)}.js`)};\n`).join("") + `await serveMigration({${migrations.map((entry, index) => `${JSON.stringify(entry.id)}: impl${index}`).join(", ")}});\n`); } generated("descriptor.quixos-package.txtpb", `# Generated by qx-scaffold-v1\npackage_id: ${JSON.stringify(packageModel.id)}\npackage_revision_id: ${JSON.stringify(packageModel.revision)}\nruntime_protocol_version: "quixos-capabilities-v1"\n` + packageModel.exports.map((entry) => `exports: { export_id: ${JSON.stringify(entry.id)} runtime_symbol: ${JSON.stringify(entry.name)} }\n`).join("")); + if (spec.initialFiles) { + if (command !== "package") throw new Error("initialFiles is only valid when creating a package"); + const authored = spec.initialFiles["package.qx"]; + if (authored !== undefined) { + const parsed = parseQx(authored); + const declaration = [...walkSyntax(parsed.root)].find(n => n.kind === "packageResourceDecl"); + const literals = declaration?.children.filter(n => n.kind === "stringLiteral") ?? []; + const identifier = declaration?.children.find(n => n.kind === "identifier"); + if (parsed.diagnostics.length || literals.length !== 2 || !identifier || authored.slice(identifier.start, identifier.end) !== spec.name || + JSON.parse(authored.slice(literals[0].start, literals[0].end)) !== spec.id || JSON.parse(authored.slice(literals[1].start, literals[1].end)) !== spec.revision) + throw new Error("Initial package declaration must match the provisioned name, ID and revision"); + } + for (const [file, content] of Object.entries(spec.initialFiles)) { + if (typeof content !== "string" || ["quixos.lock", "flake.nix", "quixos.toolchain.json", "quixos.check.json", "package.json"].includes(file)) throw new Error(`Not an initial authored file: ${file}`); + const existing = files.findIndex(entry => entry.file === prefix + file); + if (existing >= 0) files.splice(existing, 1); + create(file, content); + } + // A composed React recipe supplies its own modules and complete server. + if (reactRecipe(spec)) { + for (const file of ["src/component.tsx", "src/impl/sourceGet.ts", "descriptor.quixos-package.txtpb"]) + if (!(file in spec.initialFiles)) {const index = files.findIndex(entry => entry.file === prefix + file); if (index >= 0) files.splice(index, 1);} + } + } return {kind: "package", source: spec.source, resourceRoot: spec.directory, validation: "syntax", files}; }; + +const reactRecipe = (spec: ScaffoldRecipe) => spec.template === "typescript-react" && spec.initialFiles?.["package.qx"] && spec.initialFiles?.["src/server.ts"]; diff --git a/src/capability-language/structural-plan.ts b/src/capability-language/structural-plan.ts index 2e8fbc8..d123309 100644 --- a/src/capability-language/structural-plan.ts +++ b/src/capability-language/structural-plan.ts @@ -22,7 +22,7 @@ export type StructuralRequest = { type Change = {file: string; before: string | null; after: string; mode: number}; type Journal = {schemaVersion: 1; id: string; root: string; phase: "prepared" | "complete"; changes: Change[]}; const safeFile = (file: string) => { - if (!/^(?:[A-Za-z0-9_-][A-Za-z0-9_.-]*\/)*(?:\.gitignore|\.yarnrc.yml|[A-Za-z0-9_-][A-Za-z0-9_.-]*\.(?:qx|lock|ts|tsx|mjs|json|nix|txtpb))$/.test(file) + if (!/^(?:[A-Za-z0-9_-][A-Za-z0-9_.-]*\/)*(?:\.gitignore|\.yarnrc.yml|[A-Za-z0-9_-][A-Za-z0-9_.-]*\.(?:qx|lock|ts|tsx|css|mjs|json|nix|txtpb))$/.test(file) || file.split("/").some((part) => [".git", ".jj", ".quixos", "node_modules"].includes(part))) throw new Error(`Unsafe scaffold path ${file}`); }; const read = async (root: string, file: string): Promise => { diff --git a/src/capability-language/tool-cli.ts b/src/capability-language/tool-cli.ts index e96fc20..7a6cae1 100644 --- a/src/capability-language/tool-cli.ts +++ b/src/capability-language/tool-cli.ts @@ -2,6 +2,7 @@ import { readFile, writeFile, mkdtemp, rm, realpath } from "node:fs/promises"; import { parseQx, formatQx, lintQx } from "./source.js"; import { scaffoldAtom } from "./scaffold.js"; +import {loadQxSources} from "./source-loader.js"; import { createGitCapabilityResolver } from "./git-resolver.js"; import { planEvolution } from "../capability-model/index.js"; import { snapshotRepository } from "./candidate-check.js"; @@ -41,6 +42,39 @@ const planSummary = (plan: Awaited>) => ({ const main = async () => { const [command, ...args] = process.argv.slice(2); + if (command === "scaffold-validate-qx") { + if (args.length !== 1) throw new Error("scaffold-validate-qx SOURCES_JSON"); + const sources = JSON.parse(args[0]); + if (!Array.isArray(sources) || sources.length > 100) throw new Error("Expected at most 100 scaffold sources"); + for (const entry of sources) { + if (!entry || typeof entry.file !== "string" || typeof entry.source !== "string" || entry.source.length > 1024*1024) throw new Error("Invalid scaffold source"); + const diagnostics = parseQx(entry.source, entry.file).diagnostics; + if (diagnostics.length) throw new Error(diagnostics.map(d => `${entry.file}:${d.line}:${d.column + 1}: ${d.message}`).join("\n")); + } + process.stdout.write('{"syntaxValid":true,"verificationEvidence":false}\n'); + return; + } + if (command === "scaffold-placement-binding") { + if (args.length !== 1) throw new Error("scaffold-placement-binding ROOT"); + const {source} = await loadQxSources(args[0]); + const syntax = parseQx(source); + const text = (n: {start: number; end: number}) => source.slice(n.start, n.end); + const matches: string[] = []; + for (const edge of walkSyntax(syntax.root)) { + if (edge.kind !== "edgeDecl") continue; + for (const endpoint of edge.children.filter(n => n.kind === "edgeEndpoint")) { + const target = endpoint.children.find(n => n.kind === "targetConstraint"); + if (!target || !syntax.tokens.some(t => t.start >= target.start && t.end <= target.end && t.kind === "INTERFACE") || + !target.children.some(n => n.kind === "identifier" && text(n) === "WebStudioPlaceable")) continue; + const edgeName = text(edge.children.find(n => n.kind === "identifier")!); + const projection = text(endpoint.children.find(n => n.kind === "identifier")!); + matches.push(`bind placements.resolve to edge ${edgeName}.${projection}.resolve;`); + } + } + if (matches.length !== 1) throw new Error(`Expected one canvas placement edge for WebStudioPlaceable; found ${matches.length}. Configure the workspace canvas before adding a bundle.`); + process.stdout.write(JSON.stringify({binding: matches[0]}) + "\n"); + return; + } if (command === "package-identity") { if (args.length !== 1) throw new Error("usage: quixos-qx package-identity PACKAGE_QX"); const authored = await readFile(args[0], "utf8"); @@ -91,12 +125,14 @@ const main = async () => { if (!args.length || args.length > 2) throw new Error("usage: quixos-qx converge WORKBENCH [REGISTERED_DIRECTORY] (join package writers first)"); const context = await authoringContext(args[0]); if (command === "converge") { + console.error(`[${new Date().toISOString()}] Capture: waiting for coordinator lock (120s limit)`); const result = spawnSync("flock", ["--exclusive", "--timeout", "120", "--conflict-exit-code", "75", path.join(context.workbench, ".quixos/converge.lock"), process.execPath, process.argv[1], "_converge", context.workbench, ...(args[1] ? [args[1]] : [])], {stdio: "inherit"}); if (result.error) throw result.error; if (result.status === 75) process.stderr.write("Timed out after 120 seconds waiting for source capture; inspect the active coordinator. No build lock is held.\n"); process.exitCode = result.status ?? 1; return; } + console.error(`[${new Date().toISOString()}] Capture: coordinator lock acquired`); const result = await convergeAuthoring(context.workbench, args[1]); process.stdout.write(`${JSON.stringify(result, null, 2)}\n`); if (!result.converged) process.exitCode = 1; @@ -173,7 +209,7 @@ const main = async () => { const spec = await readSpec(specFile) as ScaffoldRecipe; if (!spec.name || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(spec.name) || !spec.id || !spec.revision || !spec.tools?.quixos) throw new Error("Interface scaffold requires name, id, revision and Quixos toolchain source"); const request: StructuralRequest = {kind: "interface", source: spec.source, files: [ - {file: "interface.qx", create: `interface ${spec.name} id ${JSON.stringify(spec.id)} revision ${JSON.stringify(spec.revision)} {\n}\n`}, + {file: "interface.qx", create: spec.declaration ?? `interface ${spec.name} id ${JSON.stringify(spec.id)} revision ${JSON.stringify(spec.revision)} {\n}\n`}, {file: "quixos.lock", create: formatQuixosLock({formatVersion: 1, quixos: {resolver: "git", ...spec.tools.quixos}, resources: []})}, {file: ".gitignore", create: ".quixos/\n"}, ]}; diff --git a/test/scaffold-recipes.test.ts b/test/scaffold-recipes.test.ts index 6b9605a..befd92f 100644 --- a/test/scaffold-recipes.test.ts +++ b/test/scaffold-recipes.test.ts @@ -22,6 +22,7 @@ test("React preset applies its browser build script and shared-platform imports" assert.match(await fs.readFile(path.join(root, "src/impl/sourceGet.ts"), "utf8"), /component.js\?browser-source/); assert.match(await fs.readFile(path.join(root, "flake.nix"), "utf8"), /browserSources = true/); assert.equal(await fs.readFile(path.join(root, "src/gen/web-studio-react-runtime.d.ts"), "utf8"), reactPlatformTypes); + assert.match(await fs.readFile(path.join(root, "src/browser-assets.d.ts"), "utf8"), /declare module "\*\.css"/); assert.equal(JSON.parse(await fs.readFile(path.join(root, "quixos.check.json"), "utf8")).options.messages["org.quixos.web-studio.ReactProps"].export, "opaqueReactPropsBinding"); await assert.rejects(fs.access(path.join(root, "quixos.scaffold.json"))); assert.equal(JSON.parse(await fs.readFile(path.join(root, "tsconfig.json"), "utf8")).compilerOptions.jsx, "react-jsx"); From 00fc2b9ff12f96becd02163e91cbf58320886e15 Mon Sep 17 00:00:00 2001 From: "Timothy J. Aveni" Date: Tue, 15 Sep 2026 15:23:24 -0700 Subject: [PATCH 05/11] Format authored monorepo code with pinned language formatters --- flake.nix | 229 ++-- nix/checked-candidate.nix | 159 ++- nix/checked-package.nix | 32 +- proto/camino/api.proto | 76 +- proto/quixos/orch.proto | 28 +- proto/quixos/runtime.proto | 4 +- src/bindings/bundle-policy.ts | 33 +- src/bindings/cli.ts | 11 +- src/bindings/client.ts | 68 +- src/bindings/index.ts | 216 +++- src/capability-language/assembly.ts | 192 ++-- src/capability-language/authoring-check.ts | 116 +- src/capability-language/authoring-context.ts | 47 +- src/capability-language/authoring-converge.ts | 175 ++- src/capability-language/authoring-inspect.ts | 85 +- src/capability-language/authoring-worklist.ts | 111 +- src/capability-language/candidate-check.ts | 202 +++- src/capability-language/checked-build.ts | 175 ++- src/capability-language/cli.ts | 68 +- src/capability-language/file-lock.ts | 49 +- src/capability-language/git-resolver.ts | 65 +- .../implementation-edit.ts | 45 +- src/capability-language/inspect-cli.ts | 7 +- src/capability-language/migration-seal.ts | 16 +- src/capability-language/parser.ts | 998 ++++++------------ src/capability-language/pin-upgrades.ts | 523 ++++++--- src/capability-language/resource-cli.ts | 63 +- src/capability-language/scaffold-recipes.ts | 396 +++++-- src/capability-language/scaffold.ts | 28 +- src/capability-language/source-loader.ts | 22 +- src/capability-language/source.ts | 64 +- src/capability-language/structural-edits.ts | 193 +++- src/capability-language/structural-plan.ts | 286 +++-- src/capability-language/tool-cli.ts | 406 +++++-- src/capability-language/workspace-cli.ts | 72 +- src/capability-model/evolution.ts | 338 ++++-- src/capability-model/migrations.ts | 116 +- src/capability-model/types.ts | 56 +- src/capability-model/validation.ts | 598 ++++------- src/descriptor-check.ts | 10 +- src/descriptor.ts | 20 +- src/resource-lock/loader.ts | 110 +- src/resource-lock/parser.ts | 173 ++- src/resource-lock/types.ts | 25 +- test/authoring-converge.test.ts | 62 +- test/authoring-inspect.test.ts | 9 +- test/authoring-worklist.test.ts | 90 +- test/bindings.test.ts | 24 +- test/bundle-policy.test.ts | 27 +- test/candidate-check.test.ts | 17 +- test/capability-assembly.test.ts | 94 +- test/capability-cli.test.ts | 13 +- test/capability-language.test.ts | 219 ++-- test/capability-model.test.ts | 148 +-- test/evolution.test.ts | 72 +- test/file-lock.test.ts | 32 +- test/fixtures/capability-model.ts | 153 +-- test/fixtures/todo.capabilities.qx | 4 +- test/fixtures/todo.package.qx | 10 +- test/fixtures/web-studio.capabilities.qx | 2 +- test/git-resolver.test.ts | 2 +- test/migrations.test.ts | 69 +- test/nix-candidate.test.ts | 91 +- test/pin-upgrades.test.ts | 207 ++-- test/qx-source.test.ts | 65 +- test/resource-lock.test.ts | 91 +- test/scaffold-recipes.test.ts | 115 +- test/structural-edits.test.ts | 85 +- test/structural-plan.test.ts | 34 +- 69 files changed, 5135 insertions(+), 3306 deletions(-) diff --git a/flake.nix b/flake.nix index 0477270..7a86d3d 100644 --- a/flake.nix +++ b/flake.nix @@ -6,126 +6,140 @@ flake-utils.url = "github:numtide/flake-utils"; }; - outputs = { self, nixpkgs, flake-utils, ... }: - flake-utils.lib.eachDefaultSystem (system: + outputs = + { + self, + nixpkgs, + flake-utils, + ... + }: + flake-utils.lib.eachDefaultSystem ( + system: let pkgs = import nixpkgs { inherit system; }; nodejs = pkgs.nodejs_24; quixos-protocol = (pkgs.callPackage ./yarn-project.nix { inherit nodejs; }) { src = pkgs.lib.cleanSource ./.; overrideAttrs = old: { - nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ - pkgs.diffutils - pkgs.esbuild - pkgs.protobuf - pkgs.git - pkgs.jujutsu - pkgs.gnutar - pkgs.util-linux - ]; - buildPhase = '' - runHook preBuild - mkdir -p "$TMPDIR/generated-before" - cp --recursive src/gen "$TMPDIR/generated-before/proto" - cp --recursive src/capability-language/generated "$TMPDIR/generated-before/capability" - cp --recursive src/resource-lock/generated "$TMPDIR/generated-before/lock" - export QUIXOS_PROTO_PATH="${pkgs.protobuf}/include:$PWD/proto''${QUIXOS_PROTO_PATH:+:$QUIXOS_PROTO_PATH}" - patchShebangs node_modules/.bin node_modules/@bufbuild/protoc-gen-es/bin - yarn build - esbuild dist/src/bindings/client.js --bundle --platform=node --target=node24 --format=esm --outfile=client-codegen.mjs - diff --recursive --unified "$TMPDIR/generated-before/proto" src/gen - diff --recursive --unified "$TMPDIR/generated-before/capability" src/capability-language/generated - diff --recursive --unified "$TMPDIR/generated-before/lock" src/resource-lock/generated - esbuild dist/src/descriptor-check.js \ - --bundle --platform=node --target=node24 --format=esm \ - --outfile=quixos-descriptor-check.mjs - esbuild dist/src/capability-language/cli.js \ - --bundle --platform=node --target=node24 --format=esm \ - --outfile=quixos-capability-compile.mjs - esbuild dist/src/capability-language/workspace-cli.js \ - --bundle --platform=node --target=node24 --format=esm \ - --outfile=quixos-workspace-compile.mjs - esbuild dist/src/capability-language/resource-cli.js \ - --bundle --platform=node --target=node24 --format=esm \ - --outfile=quixos-resource-compile.mjs - esbuild dist/src/resource-lock/cli.js \ - --bundle --platform=node --target=node24 --format=esm \ - --outfile=quixos-lock-check.mjs - esbuild dist/src/bindings/cli.js --bundle --platform=node --target=node24 --format=esm --outfile=quixos-codegen-ts.mjs - esbuild dist/src/bindings/react-platform.js --bundle --platform=node --target=node24 --format=esm --outfile=react-platform.mjs - esbuild dist/src/capability-language/tool-cli.js --bundle --platform=node --target=node24 --format=esm --outfile=quixos-qx.mjs - runHook postBuild - ''; - doCheck = true; - checkPhase = '' - runHook preCheck - yarn test - runHook postCheck - ''; - installPhase = '' - runHook preInstall - mkdir -p "$out" - install -Dm644 nix/checked-package.nix "$out/share/checked-package.nix" - substitute nix/checked-candidate.nix "$out/share/checked-candidate.nix" \ - --replace-fail '@nixpkgs@' '${pkgs.path}' - cp --reflink=auto --recursive grammar "$out/grammar" - cp --reflink=auto --recursive proto "$out/proto" - cp --reflink=auto --recursive dist "$out/dist" - cp package.json "$out/package.json" - mkdir -p "$out/bin" - for tool in quixos-codegen-ts quixos-qx; do - printf '#!/bin/sh\nexec ${pkgs.nodejs_24}/bin/node "%s/libexec/quixos-protocol/%s.mjs" "$@"\n' "$out" "$tool" > "$out/bin/$tool" - chmod +x "$out/bin/$tool" - done - cat > "$out/bin/quixos-descriptor-check" < "$out/bin/quixos-capability-compile" < "$out/bin/quixos-workspace-compile" < "$out/bin/quixos-resource-compile" < "$out/bin/quixos-lock-check" < "$out/bin/$tool" + chmod +x "$out/bin/$tool" + done + cat > "$out/bin/quixos-descriptor-check" < "$out/bin/quixos-capability-compile" < "$out/bin/quixos-workspace-compile" < "$out/bin/quixos-resource-compile" < "$out/bin/quixos-lock-check" <= 100 then throw "Source dependency depth exceeds 100" - else let - directory = fetch source; - lockFile = pkgs.runCommand "qx-source-lock.json" { } '' - ${protocol}/bin/quixos-lock-check ${directory}/quixos.lock > "$out" - ''; - lock = builtins.fromJSON (builtins.readFile lockFile); - children = map (entry: load (ancestors ++ [ (sourceKey source) ]) (entry.source // { inherit (entry) kind; })) lock.resources; - in source // { inherit directory children; }; + else + let + directory = fetch source; + lockFile = pkgs.runCommand "qx-source-lock.json" { } '' + ${protocol}/bin/quixos-lock-check ${directory}/quixos.lock > "$out" + ''; + lock = builtins.fromJSON (builtins.readFile lockFile); + children = map ( + entry: load (ancestors ++ [ (sourceKey source) ]) (entry.source // { inherit (entry) kind; }) + ) lock.resources; + in + source // { inherit directory children; }; root = load [ ] { inherit kind repository commit; }; flatten = node: [ node ] ++ pkgs.lib.concatMap flatten node.children; - nodes = builtins.attrValues (builtins.listToAttrs (map (node: { - name = sourceKey node; value = node; - }) (flatten root))); - snapshots = pkgs.writeText "qx-nix-source-graph.json" (builtins.toJSON { - resources = map (node: { inherit (node) kind repository commit directory; }) - (builtins.filter (node: node.kind != "workspace") nodes); - }); - compile = node: pkgs.runCommand "qx-${node.kind}-contract" { } '' - mkdir -p "$out" - ${if node.kind == "workspace" then '' - ${protocol}/bin/quixos-workspace-compile --root ${node.directory} \ - --source-root-commit ${pkgs.lib.escapeShellArg node.commit} \ - --checkout-root "$TMPDIR/checkouts" --snapshot-map ${snapshots} \ - --graph-out "$out/graph.json" > "$out/candidate.json" - '' else '' - ${protocol}/bin/quixos-resource-compile --root ${node.directory} \ - --kind ${node.kind} --repository ${pkgs.lib.escapeShellArg node.repository} \ - --commit ${pkgs.lib.escapeShellArg node.commit} \ - --checkout-root "$TMPDIR/checkouts" --snapshot-map ${snapshots} --snapshot-only true \ - --graph-out "$out/graph.json" --schema-out "$out/bindings.json" > "$out/candidate.json" - ''} - ''; + nodes = builtins.attrValues ( + builtins.listToAttrs ( + map (node: { + name = sourceKey node; + value = node; + }) (flatten root) + ) + ); + snapshots = pkgs.writeText "qx-nix-source-graph.json" ( + builtins.toJSON { + resources = map (node: { + inherit (node) + kind + repository + commit + directory + ; + }) (builtins.filter (node: node.kind != "workspace") nodes); + } + ); + compile = + node: + pkgs.runCommand "qx-${node.kind}-contract" { } '' + mkdir -p "$out" + ${ + if node.kind == "workspace" then + '' + ${protocol}/bin/quixos-workspace-compile --root ${node.directory} \ + --source-root-commit ${pkgs.lib.escapeShellArg node.commit} \ + --checkout-root "$TMPDIR/checkouts" --snapshot-map ${snapshots} \ + --graph-out "$out/graph.json" > "$out/candidate.json" + '' + else + '' + ${protocol}/bin/quixos-resource-compile --root ${node.directory} \ + --kind ${node.kind} --repository ${pkgs.lib.escapeShellArg node.repository} \ + --commit ${pkgs.lib.escapeShellArg node.commit} \ + --checkout-root "$TMPDIR/checkouts" --snapshot-map ${snapshots} --snapshot-only true \ + --graph-out "$out/graph.json" --schema-out "$out/bindings.json" > "$out/candidate.json" + '' + } + ''; contract = compile root; - checkPackage = node: let - compiled = compile node; - candidate = builtins.fromJSON (builtins.readFile "${compiled}/candidate.json"); - package = builtins.getFlake ("git+${node.repository}?rev=${node.commit}&ref=refs/tags/quixos-reachability/${node.commit}"); - checked = package.quixosPackages.${system}.checkedServer or - (throw "Package ${node.repository} lacks checkedServer; use the supported package scaffold."); - artifact = checked { - schema = "${compiled}/bindings.json"; - generator = protocol; + checkPackage = + node: + let + compiled = compile node; + candidate = builtins.fromJSON (builtins.readFile "${compiled}/candidate.json"); + package = builtins.getFlake ( + "git+${node.repository}?rev=${node.commit}&ref=refs/tags/quixos-reachability/${node.commit}" + ); + checked = + package.quixosPackages.${system}.checkedServer + or (throw "Package ${node.repository} lacks checkedServer; use the supported package scaffold."); + artifact = checked { + schema = "${compiled}/bindings.json"; + generator = protocol; + packageRevisionId = candidate.revision.revisionId; + }; + in + { packageRevisionId = candidate.revision.revisionId; + artifactPath = artifact; }; - in { packageRevisionId = candidate.revision.revisionId; artifactPath = artifact; }; - checks = if contractOnly then [ ] else map checkPackage (builtins.filter (node: node.kind == "package") nodes); + checks = + if contractOnly then + [ ] + else + map checkPackage (builtins.filter (node: node.kind == "package") nodes); manifest = pkgs.writeText "qx-candidate-checks.json" (builtins.toJSON checks); - bindingOptions = pkgs.writeText "qx-typescript-options.json" (builtins.toJSON - ((builtins.fromJSON (builtins.readFile "${root.directory}/quixos.check.json")).options or { })); -in pkgs.runCommand (if contractOnly then "qx-contract" else "qx-checked-candidate") { } '' + bindingOptions = pkgs.writeText "qx-typescript-options.json" ( + builtins.toJSON ( + (builtins.fromJSON (builtins.readFile "${root.directory}/quixos.check.json")).options or { } + ) + ); +in +pkgs.runCommand (if contractOnly then "qx-contract" else "qx-checked-candidate") { } '' mkdir -p "$out" cp ${contract}/candidate.json "$out/candidate.json" cp ${contract}/graph.json "$out/graph.json" cp ${manifest} "$out/checks.json" - ${pkgs.lib.optionalString (kind != "workspace") ''cp ${contract}/bindings.json "$out/bindings.json"''} + ${pkgs.lib.optionalString ( + kind != "workspace" + ) ''cp ${contract}/bindings.json "$out/bindings.json"''} ${pkgs.lib.optionalString (kind == "package") '' ${protocol}/bin/quixos-codegen-ts ${contract}/bindings.json \ ${pkgs.lib.escapeShellArg (builtins.fromJSON (builtins.readFile "${contract}/candidate.json")).revision.revisionId} \ diff --git a/nix/checked-package.nix b/nix/checked-package.nix index e6a79bc..39b16f8 100644 --- a/nix/checked-package.nix +++ b/nix/checked-package.nix @@ -1,17 +1,35 @@ # One derivation path for provisional checking and activation. The source and # schema come from exact committed inputs resolved by the Quixos compiler. -{ source, schema, generator, packageRevisionId, system ? builtins.currentSystem }: +{ + source, + schema, + generator, + packageRevisionId, + system ? builtins.currentSystem, +}: let packageSource = builtins.path { path = /. + source; name = "quixos-package-source"; - filter = path: _: let name = baseNameOf path; in name != ".git" && name != ".jj"; + filter = + path: _: + let + name = baseNameOf path; + in + name != ".git" && name != ".jj"; }; - package = builtins.getFlake ("path:" + builtins.unsafeDiscardStringContext (toString packageSource)); - checked = package.quixosPackages.${system}.checkedServer or - (throw "Package ${packageRevisionId} lacks checkedServer; use the supported package scaffold."); -in checked { + package = builtins.getFlake ( + "path:" + builtins.unsafeDiscardStringContext (toString packageSource) + ); + checked = + package.quixosPackages.${system}.checkedServer + or (throw "Package ${packageRevisionId} lacks checkedServer; use the supported package scaffold."); +in +checked { inherit packageRevisionId; - schema = builtins.path { path = /. + schema; name = "candidate-package-bindings.json"; }; + schema = builtins.path { + path = /. + schema; + name = "candidate-package-bindings.json"; + }; generator = builtins.storePath generator; } diff --git a/proto/camino/api.proto b/proto/camino/api.proto index 9f82e11..f277459 100644 --- a/proto/camino/api.proto +++ b/proto/camino/api.proto @@ -22,9 +22,15 @@ service CaminoService { } message NullValue {} -message ObjectValue { map fields = 1; } -message ListValue { repeated Value values = 1; } -message RefValue { string object_id = 1; } +message ObjectValue { + map fields = 1; +} +message ListValue { + repeated Value values = 1; +} +message RefValue { + string object_id = 1; +} message CrdtValue { string type = 1; string encoding = 2; @@ -43,7 +49,9 @@ message StateValueSource { CrdtValue crdt_snapshot = 6; } -message ValueSource { StateValueSource state = 1; } +message ValueSource { + StateValueSource state = 1; +} message Value { oneof kind { @@ -61,26 +69,44 @@ message Value { ValueSource source = 11; } -message InstallPersistencePlanRequest { PersistencePlan plan = 1; } -message InstallPersistencePlanResponse { PersistencePlan plan = 1; } +message InstallPersistencePlanRequest { + PersistencePlan plan = 1; +} +message InstallPersistencePlanResponse { + PersistencePlan plan = 1; +} message GetPersistencePlanRequest {} -message GetPersistencePlanResponse { PersistencePlan plan = 1; } +message GetPersistencePlanResponse { + PersistencePlan plan = 1; +} message CreateObjectRequest { string atom_id = 1; map initial_state = 2; } -message CreateObjectResponse { CaminoObject object = 1; } -message GetObjectRequest { string object_id = 1; } -message GetObjectResponse { CaminoObject object = 1; } -message ListObjectsRequest { string atom_id = 1; } -message ListObjectsResponse { repeated CaminoObject objects = 1; } +message CreateObjectResponse { + CaminoObject object = 1; +} +message GetObjectRequest { + string object_id = 1; +} +message GetObjectResponse { + CaminoObject object = 1; +} +message ListObjectsRequest { + string atom_id = 1; +} +message ListObjectsResponse { + repeated CaminoObject objects = 1; +} message ReadStateRequest { string object_id = 1; string slot_id = 2; } -message ReadStateResponse { Value value = 1; } +message ReadStateResponse { + Value value = 1; +} message WriteStateRequest { string object_id = 1; string slot_id = 2; @@ -100,15 +126,23 @@ message ConnectEdgeRequest { optional int32 ordinal = 5; optional int32 target_ordinal = 6; } -message ConnectEdgeResponse { CaminoEdge edge = 1; } +message ConnectEdgeResponse { + CaminoEdge edge = 1; +} message ResolveEdgeRequest { string object_id = 1; string edge_type_id = 2; string projection_id = 3; } -message ResolveEdgeResponse { repeated CaminoEdge edges = 1; } -message DisconnectEdgeRequest { string edge_id = 1; } -message DisconnectEdgeResponse { string edge_id = 1; } +message ResolveEdgeResponse { + repeated CaminoEdge edges = 1; +} +message DisconnectEdgeRequest { + string edge_id = 1; +} +message DisconnectEdgeResponse { + string edge_id = 1; +} message CollectionEntry { // Existing entry identity to preserve; empty allocates a new canonical edge. @@ -128,8 +162,12 @@ message ReplaceCollectionRequest { repeated CollectionEntry entries = 5; } -message ListOpsRequest { string object_id = 1; } -message ListOpsResponse { repeated CaminoOp ops = 1; } +message ListOpsRequest { + string object_id = 1; +} +message ListOpsResponse { + repeated CaminoOp ops = 1; +} message WatchObjectRequest { string object_id = 1; string after_op_id = 2; diff --git a/proto/quixos/orch.proto b/proto/quixos/orch.proto index 7ce44cf..bf306c2 100644 --- a/proto/quixos/orch.proto +++ b/proto/quixos/orch.proto @@ -11,7 +11,8 @@ service OrchestratorRuntime { rpc InvokeCapability(InvokeCapabilityRequest) returns (InvokeCapabilityResponse); rpc WatchCapability(WatchCapabilityRequest) returns (stream WatchCapabilityEvent); rpc ConstructObject(ConstructObjectRequest) returns (ConstructObjectResponse); - rpc ResolveOrConstructRelatedObject(ResolveOrConstructRelatedObjectRequest) returns (ResolveOrConstructRelatedObjectResponse); + rpc ResolveOrConstructRelatedObject(ResolveOrConstructRelatedObjectRequest) + returns (ResolveOrConstructRelatedObjectResponse); rpc GetWorkspace(GetWorkspaceRequest) returns (GetWorkspaceResponse); rpc ListPackageDescriptors(ListPackageDescriptorsRequest) returns (ListPackageDescriptorsResponse); rpc ListPackageRuntimes(ListPackageRuntimesRequest) returns (ListPackageRuntimesResponse); @@ -23,7 +24,9 @@ message ConstructObjectRequest { string atom_id = 1; map input = 2; } -message ConstructObjectResponse { camino.CaminoObject object = 1; } +message ConstructObjectResponse { + camino.CaminoObject object = 1; +} message ResolveOrConstructRelatedObjectRequest { string object_id = 1; @@ -89,12 +92,23 @@ message ConstructorInputContract { } message ListActivationsRequest {} message ListPackageDescriptorsRequest {} -message ListPackageDescriptorsResponse { repeated quixos.PackageDescriptor descriptors = 1; } +message ListPackageDescriptorsResponse { + repeated quixos.PackageDescriptor descriptors = 1; +} message ListPackageRuntimesRequest {} -message ListPackageRuntimesResponse { repeated PackageRuntimeStatus runtimes = 1; } -message ListActivationsResponse { repeated Activation activations = 1; } -message CloseActivationRequest { string activation_id = 1; string reason = 2; } -message CloseActivationResponse { Activation activation = 1; } +message ListPackageRuntimesResponse { + repeated PackageRuntimeStatus runtimes = 1; +} +message ListActivationsResponse { + repeated Activation activations = 1; +} +message CloseActivationRequest { + string activation_id = 1; + string reason = 2; +} +message CloseActivationResponse { + Activation activation = 1; +} message Activation { string activation_id = 1; diff --git a/proto/quixos/runtime.proto b/proto/quixos/runtime.proto index 3672cb0..ba1ca50 100644 --- a/proto/quixos/runtime.proto +++ b/proto/quixos/runtime.proto @@ -35,7 +35,9 @@ message InvocationContext { // Host-selected owner; packages must not invent workspace-local ownership. string owner_conformance_id = 6; } -message InvocationControlRequest { string invocation_id = 1; } +message InvocationControlRequest { + string invocation_id = 1; +} message InvocationStatus { string invocation_id = 1; // unknown, running, cancellation-requested, completed, failed diff --git a/src/bindings/bundle-policy.ts b/src/bindings/bundle-policy.ts index c63e82f..8221a4d 100644 --- a/src/bindings/bundle-policy.ts +++ b/src/bindings/bundle-policy.ts @@ -1,25 +1,35 @@ -import {parse} from "@babel/parser"; +import { parse } from "@babel/parser"; import fs from "node:fs/promises"; import path from "node:path"; /** Enforce the authored bundled-code contract, not a security sandbox. */ export function bundlePolicyErrors(source: string, filename: string): string[] { - const ast = parse(source, {sourceType: "module", plugins: ["typescript", "jsx"]}); + const ast = parse(source, { sourceType: "module", plugins: ["typescript", "jsx"] }); const errors: string[] = []; const visit = (value: unknown) => { - if (Array.isArray(value)) {value.forEach(visit); return;} + if (Array.isArray(value)) { + value.forEach(visit); + return; + } if (!value || typeof value !== "object") return; const n = value as Record; if (typeof n.type !== "string") return; const fail = (message: string) => errors.push(`${filename}:${n.loc?.start.line ?? 1}: ${message}`); - if (n.type === "MetaProperty" && n.meta.name === "import") fail("Bundled package code cannot use import.meta; import packaged assets statically"); - if (n.type === "Identifier" && ["__dirname", "__filename"].includes(n.name)) fail("Bundled package code cannot depend on module filesystem locations"); + if (n.type === "MetaProperty" && n.meta.name === "import") + fail("Bundled package code cannot use import.meta; import packaged assets statically"); + if (n.type === "Identifier" && ["__dirname", "__filename"].includes(n.name)) + fail("Bundled package code cannot depend on module filesystem locations"); if (["CallExpression", "NewExpression"].includes(n.type)) { - if (n.callee?.type === "Identifier" && ["eval", "Function"].includes(n.callee.name)) fail("Dynamic code generation is unsupported in bundled package code"); - if ((n.callee?.type === "Import" || (n.callee?.type === "Identifier" && n.callee.name === "require")) && - (n.arguments.length !== 1 || n.arguments[0].type !== "StringLiteral")) fail("Module imports must have a static string specifier"); + if (n.callee?.type === "Identifier" && ["eval", "Function"].includes(n.callee.name)) + fail("Dynamic code generation is unsupported in bundled package code"); + if ( + (n.callee?.type === "Import" || (n.callee?.type === "Identifier" && n.callee.name === "require")) && + (n.arguments.length !== 1 || n.arguments[0].type !== "StringLiteral") + ) + fail("Module imports must have a static string specifier"); } - if (n.type === "ImportExpression" && n.source.type !== "StringLiteral") fail("Module imports must have a static string specifier"); + if (n.type === "ImportExpression" && n.source.type !== "StringLiteral") + fail("Module imports must have a static string specifier"); Object.values(n).forEach(visit); }; visit(ast); @@ -28,12 +38,13 @@ export function bundlePolicyErrors(source: string, filename: string): string[] { export async function checkBundleSources(directory: string): Promise { const errors: string[] = []; async function walk(current: string) { - for (const entry of await fs.readdir(current, {withFileTypes: true})) { + for (const entry of await fs.readdir(current, { withFileTypes: true })) { if (["gen", "node_modules"].includes(entry.name)) continue; const file = path.join(current, entry.name); if (entry.isSymbolicLink()) throw new Error(`Authored source symlinks are unsupported: ${file}`); if (entry.isDirectory()) await walk(file); - else if (/\.[cm]?[jt]sx?$/.test(entry.name) && !entry.name.endsWith(".d.ts")) errors.push(...bundlePolicyErrors(await fs.readFile(file, "utf8"), file)); + else if (/\.[cm]?[jt]sx?$/.test(entry.name) && !entry.name.endsWith(".d.ts")) + errors.push(...bundlePolicyErrors(await fs.readFile(file, "utf8"), file)); } } await walk(directory); diff --git a/src/bindings/cli.ts b/src/bindings/cli.ts index 21f34fc..df81397 100644 --- a/src/bindings/cli.ts +++ b/src/bindings/cli.ts @@ -2,12 +2,12 @@ import { readFile, writeFile } from "node:fs/promises"; import { generateTypeScriptBindings } from "./index.js"; import path from "node:path"; -import {reactPlatformTypes} from "./react-platform.js"; +import { reactPlatformTypes } from "./react-platform.js"; const main = async () => { const [schema, revision, output, options, ...rest] = process.argv.slice(2); - if (!schema || !revision || !output || rest.length) throw new Error( - "usage: quixos-codegen-ts SCHEMA.json PACKAGE_REVISION OUTPUT.ts [OPTIONS.json]"); + if (!schema || !revision || !output || rest.length) + throw new Error("usage: quixos-codegen-ts SCHEMA.json PACKAGE_REVISION OUTPUT.ts [OPTIONS.json]"); const config = options ? JSON.parse(await readFile(options, "utf8")) : {}; const generated = generateTypeScriptBindings(JSON.parse(await readFile(schema, "utf8")), revision, config); await writeFile(output, generated); @@ -15,4 +15,7 @@ const main = async () => { await writeFile(path.join(path.dirname(output), "web-studio-react-runtime.d.ts"), reactPlatformTypes); } }; -main().catch((error: unknown) => { console.error(error instanceof Error ? error.message : error); process.exitCode = 1; }); +main().catch((error: unknown) => { + console.error(error instanceof Error ? error.message : error); + process.exitCode = 1; +}); diff --git a/src/bindings/client.ts b/src/bindings/client.ts index 26ae37e..af56ad3 100644 --- a/src/bindings/client.ts +++ b/src/bindings/client.ts @@ -1,16 +1,33 @@ -import type {InterfaceRevision, ValueType} from "../capability-model/types.js"; +import type { InterfaceRevision, ValueType } from "../capability-model/types.js"; /** Host clients have no package receiver, but must use the same checked * interface signatures and argument framing as generated package ports. */ export const generateClientContracts = (interfaces: InterfaceRevision[], messages: Record) => { const type = (value: ValueType): string => { switch (value.kind) { - case "builtin": return value.name === "unit" ? "undefined" : "string"; - case "scalar": return ({bool: "boolean", string: "string", bytes: "Uint8Array", int32: "number", uint32: "number", double: "number", int64: "bigint", uint64: "bigint"})[value.name]; - case "object-ref": return `{readonly $quixosRef: string}`; - case "optional": return `(${type(value.value)} | null)`; - case "list": return `Array<${type(value.value)}>`; - case "record": return `{${Object.entries(value.fields).map(([name, field]) => `${JSON.stringify(name)}${field.kind === "optional" ? "?" : ""}: ${type(field)}`).join("; ")}}`; + case "builtin": + return value.name === "unit" ? "undefined" : "string"; + case "scalar": + return { + bool: "boolean", + string: "string", + bytes: "Uint8Array", + int32: "number", + uint32: "number", + double: "number", + int64: "bigint", + uint64: "bigint", + }[value.name]; + case "object-ref": + return `{readonly $quixosRef: string}`; + case "optional": + return `(${type(value.value)} | null)`; + case "list": + return `Array<${type(value.value)}>`; + case "record": + return `{${Object.entries(value.fields) + .map(([name, field]) => `${JSON.stringify(name)}${field.kind === "optional" ? "?" : ""}: ${type(field)}`) + .join("; ")}}`; case "message": { const binding = messages[value.descriptorId]; if (!binding) throw new Error(`Missing host message type ${value.descriptorId}`); @@ -18,12 +35,33 @@ export const generateClientContracts = (interfaces: InterfaceRevision[], message } } }; - const operations = interfaces.flatMap(iface => iface.members.flatMap(member => member.operations - .filter(operation => operation.mode === "call").map(operation => ({...operation, interfaceRevisionId: iface.revisionId})))); - return `// Generated from checked QX interfaces. Regenerate with scripts/generate-platform-contracts.mjs.\n` + - `export type PlatformInputs = {\n${operations.map(operation => ` ${JSON.stringify(operation.id)}: ${type(operation.inputType)};`).join("\n")}\n};\n` + - `export const platformOperations = ${JSON.stringify(Object.fromEntries(operations.map(operation => [operation.id, { - interfaceRevisionId: operation.interfaceRevisionId, - input: operation.inputType.kind === "builtin" && operation.inputType.name === "unit" ? "unit" : ["record", "message"].includes(operation.inputType.kind) ? "fields" : "value", - }])), null, 2)} as const;\n`; + const operations = interfaces.flatMap((iface) => + iface.members.flatMap((member) => + member.operations + .filter((operation) => operation.mode === "call") + .map((operation) => ({ ...operation, interfaceRevisionId: iface.revisionId })), + ), + ); + return ( + `// Generated from checked QX interfaces. Regenerate with scripts/generate-platform-contracts.mjs.\n` + + `export type PlatformInputs = {\n${operations.map((operation) => ` ${JSON.stringify(operation.id)}: ${type(operation.inputType)};`).join("\n")}\n};\n` + + `export const platformOperations = ${JSON.stringify( + Object.fromEntries( + operations.map((operation) => [ + operation.id, + { + interfaceRevisionId: operation.interfaceRevisionId, + input: + operation.inputType.kind === "builtin" && operation.inputType.name === "unit" + ? "unit" + : ["record", "message"].includes(operation.inputType.kind) + ? "fields" + : "value", + }, + ]), + ), + null, + 2, + )} as const;\n` + ); }; diff --git a/src/bindings/index.ts b/src/bindings/index.ts index 0eda305..ec9f78d 100644 --- a/src/bindings/index.ts +++ b/src/bindings/index.ts @@ -9,9 +9,12 @@ export type BindingSchema = { packages: PackageRevision[]; }; export const bindingSchema = (compiled: CompiledCapabilityResourceRepository): BindingSchema => ({ - format: "quixos-bindings", version: 1, - interfaces: compiled.resources.flatMap((node) => node.resource.kind === "interface" ? [node.resource.revision] : []), - packages: compiled.resources.flatMap((node) => node.resource.kind === "package" ? [node.resource.revision] : []), + format: "quixos-bindings", + version: 1, + interfaces: compiled.resources.flatMap((node) => + node.resource.kind === "interface" ? [node.resource.revision] : [], + ), + packages: compiled.resources.flatMap((node) => (node.resource.kind === "package" ? [node.resource.revision] : [])), }); export type TypeScriptBindingOptions = { runtimeModule?: string; @@ -19,33 +22,61 @@ export type TypeScriptBindingOptions = { messages?: Record; }; export function generatePackageDescriptor(schema: BindingSchema, revisionId: string): string { - const pkg = schema.packages.find(entry => entry.revisionId === revisionId); + const pkg = schema.packages.find((entry) => entry.revisionId === revisionId); if (!pkg) throw new Error(`Unknown package revision ${revisionId}`); - return `# Generated from the checked package contract\npackage_id: ${JSON.stringify(pkg.packageId)}\npackage_revision_id: ${JSON.stringify(pkg.revisionId)}\nruntime_protocol_version: "quixos-capabilities-v1"\n` + - pkg.exports.map(entry => `exports: { export_id: ${JSON.stringify(entry.id)} runtime_symbol: ${JSON.stringify(entry.displayName)} }\n`).join(""); + return ( + `# Generated from the checked package contract\npackage_id: ${JSON.stringify(pkg.packageId)}\npackage_revision_id: ${JSON.stringify(pkg.revisionId)}\nruntime_protocol_version: "quixos-capabilities-v1"\n` + + pkg.exports + .map( + (entry) => + `exports: { export_id: ${JSON.stringify(entry.id)} runtime_symbol: ${JSON.stringify(entry.displayName)} }\n`, + ) + .join("") + ); } const q = JSON.stringify; -const object = (entries: [string, string][]) => `{ ${entries.map(([key, value]) => `${q(key)}: ${value}`).join("; ")} }`; +const object = (entries: [string, string][]) => + `{ ${entries.map(([key, value]) => `${q(key)}: ${value}`).join("; ")} }`; const unit = (type: ValueType) => type.kind === "builtin" && type.name === "unit"; export const generateTypeScriptBindings = ( - schema: BindingSchema, packageRevisionId: string, options: TypeScriptBindingOptions = {}, + schema: BindingSchema, + packageRevisionId: string, + options: TypeScriptBindingOptions = {}, ) => { - if (schema.format !== "quixos-bindings" || schema.version !== 1) throw new Error("Unsupported binding schema version"); + if (schema.format !== "quixos-bindings" || schema.version !== 1) + throw new Error("Unsupported binding schema version"); const pkg = schema.packages.find((entry) => entry.revisionId === packageRevisionId); if (!pkg) throw new Error(`Unknown package revision ${packageRevisionId}`); const messages = new Map(); const type = (value: ValueType): string => { switch (value.kind) { - case "builtin": return value.name === "unit" ? "null" : "QxWatchHandle"; - case "scalar": return ({ bool: "boolean", bytes: "Uint8Array", string: "string", int64: "bigint", uint64: "bigint", - double: "number", int32: "number", uint32: "number" })[value.name]; - case "object-ref": return `QxObjectRef<${q(value.expectation.kind === "atom" ? `atom:${value.expectation.atomId}` : `interface:${value.expectation.interfaceRevisionId}`)}>`; - case "optional": return `(${type(value.value)} | null)`; - case "list": return `Array<${type(value.value)}>`; - case "record": return `{ ${Object.entries(value.fields).map(([name, field]) => `${q(name)}: ${type(field)}`).join("; ")} }`; + case "builtin": + return value.name === "unit" ? "null" : "QxWatchHandle"; + case "scalar": + return { + bool: "boolean", + bytes: "Uint8Array", + string: "string", + int64: "bigint", + uint64: "bigint", + double: "number", + int32: "number", + uint32: "number", + }[value.name]; + case "object-ref": + return `QxObjectRef<${q(value.expectation.kind === "atom" ? `atom:${value.expectation.atomId}` : `interface:${value.expectation.interfaceRevisionId}`)}>`; + case "optional": + return `(${type(value.value)} | null)`; + case "list": + return `Array<${type(value.value)}>`; + case "record": + return `{ ${Object.entries(value.fields) + .map(([name, field]) => `${q(name)}: ${type(field)}`) + .join("; ")} }`; case "message": { - if (!options.messages?.[value.descriptorId]) throw new Error(`Missing TypeScript message binding for ${value.descriptorId}`); + if (!options.messages?.[value.descriptorId]) + throw new Error(`Missing TypeScript message binding for ${value.descriptorId}`); if (!messages.has(value.descriptorId)) messages.set(value.descriptorId, `message${messages.size}`); return `BindingValue`; } @@ -53,7 +84,7 @@ export const generateTypeScriptBindings = ( }; const ref = (target: { kind: "atom"; atomId: string } | { kind: "interface"; interfaceRevisionId: string }) => `QxObjectRef<${q(target.kind === "atom" ? `atom:${target.atomId}` : `interface:${target.interfaceRevisionId}`)}>`; - const params = (input: ValueType) => unit(input) ? "" : `input: ${type(input)}`; + const params = (input: ValueType) => (unit(input) ? "" : `input: ${type(input)}`); const port = (entry: DependencyPort): { type: string; spec: unknown } => { const requirement = entry.requirement; switch (requirement.kind) { @@ -69,36 +100,76 @@ export const generateTypeScriptBindings = ( case "edge": { const methods = requirement.primitives.map((primitive): [string, string] => { if (primitive === "resolve") return [primitive, `() => Promise>`]; - if (primitive === "connect" || primitive === "disconnect") return [primitive, `(target: ${ref(requirement.target)}) => Promise`]; + if (primitive === "connect" || primitive === "disconnect") + return [primitive, `(target: ${ref(requirement.target)}) => Promise`]; throw new Error(`Edge primitive ${primitive} is not supported by the TypeScript runtime binding yet`); }); - if (requirement.primitives.includes("resolve")) methods.push(["collection", `() => Promise>`]); - if (["resolve", "connect", "disconnect"].every((primitive) => requirement.primitives.includes(primitive as "resolve"))) methods.push(["replace", `(entries: RelationshipEntry<${ref(requirement.target)}>[], expectedRevision: bigint) => Promise>`]); + if (requirement.primitives.includes("resolve")) + methods.push(["collection", `() => Promise>`]); + if ( + ["resolve", "connect", "disconnect"].every((primitive) => + requirement.primitives.includes(primitive as "resolve"), + ) + ) + methods.push([ + "replace", + `(entries: RelationshipEntry<${ref(requirement.target)}>[], expectedRevision: bigint) => Promise>`, + ]); return { type: object(methods), spec: { kind: "edge", id: entry.id, primitives: requirement.primitives } }; } case "interface": { - const contract = schema.interfaces.find((candidate) => candidate.revisionId === requirement.interfaceRevisionId); + const contract = schema.interfaces.find( + (candidate) => candidate.revisionId === requirement.interfaceRevisionId, + ); if (!contract) throw new Error(`Missing imported interface contract ${requirement.interfaceRevisionId}`); // Streaming ports need a future streaming ABI; ordinary calls are fully typed today. - const operations = contract.members.flatMap((member) => member.operations.filter((operation) => operation.mode === "call") - .map((operation) => ({ ...operation, name: `${member.displayName}.${operation.displayName}` }))); - return { type: object([ - ["objectId", ref({ kind: "interface", interfaceRevisionId: requirement.interfaceRevisionId })], - ["live", object(operations.map(operation => [operation.name, `(${params(operation.inputType)}) => Promise`]))], - ...operations.map((operation): [string, string] => [operation.name, `(${params(operation.inputType)}) => Promise<${type(operation.outputType)}>`]), - ]), - spec: { kind: "interface", id: entry.id, operations: Object.fromEntries(operations.map(({name, id, inputType, outputType}) => - [name, {id, inputType, outputType}])) } }; + const operations = contract.members.flatMap((member) => + member.operations + .filter((operation) => operation.mode === "call") + .map((operation) => ({ ...operation, name: `${member.displayName}.${operation.displayName}` })), + ); + return { + type: object([ + ["objectId", ref({ kind: "interface", interfaceRevisionId: requirement.interfaceRevisionId })], + [ + "live", + object( + operations.map((operation) => [ + operation.name, + `(${params(operation.inputType)}) => Promise`, + ]), + ), + ], + ...operations.map((operation): [string, string] => [ + operation.name, + `(${params(operation.inputType)}) => Promise<${type(operation.outputType)}>`, + ]), + ]), + spec: { + kind: "interface", + id: entry.id, + operations: Object.fromEntries( + operations.map(({ name, id, inputType, outputType }) => [name, { id, inputType, outputType }]), + ), + }, + }; } case "constructor": { const input = requirement.inputType; - if (!input) throw new Error(`Constructor port ${entry.id} needs an explicit input contract: add 'input TYPE' after ${requirement.atomId} in QX`); - return { type: object([["construct", `(${params(input)}) => Promise<${ref({ kind: "atom", atomId: requirement.atomId })}>`]]), - spec: { kind: "constructor", id: entry.id, inputType: input } }; + if (!input) + throw new Error( + `Constructor port ${entry.id} needs an explicit input contract: add 'input TYPE' after ${requirement.atomId} in QX`, + ); + return { + type: object([ + ["construct", `(${params(input)}) => Promise<${ref({ kind: "atom", atomId: requirement.atomId })}>`], + ]), + spec: { kind: "constructor", id: entry.id, inputType: input }, + }; } } }; - const exports = [...pkg.exports].sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0); + const exports = [...pkg.exports].sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)); const names = new Set(); const specs: Record = {}; const contexts: [string, string][] = []; @@ -107,38 +178,75 @@ export const generateTypeScriptBindings = ( if (names.has(entry.displayName)) throw new Error(`Duplicate export name ${entry.displayName}`); names.add(entry.displayName); const ports = entry.dependencyPorts.map((dependency) => ({ name: dependency.displayName, ...port(dependency) })); - if (new Set(ports.map((p) => p.name)).size !== ports.length) throw new Error(`Duplicate dependency name in ${entry.displayName}`); - const receiver = entry.kind === "constructor" ? ref({ kind: "atom", atomId: entry.constructsAtom }) : - entry.kind === "operation" && entry.receiverRequirement.kind === "exact-atom" ? - ref({ kind: "atom", atomId: entry.receiverRequirement.atomId }) : - entry.kind === "operation" && entry.receiverRequirement.kind === "all-interfaces" ? - `QxObjectRef<${entry.receiverRequirement.interfaceRevisionIds.map((id) => q(`interface:${id}`)).join(" | ") || "never"}>` : "QxObjectRef"; - const contextShape = object([["objectId", receiver], ["input", type(entry.inputType)], - ["ports", object(ports.map((port) => [port.name, port.type]))]]); - contexts.push([entry.displayName, `${contextShape} & QxContextLifecycle<${contextShape} & {signal?: AbortSignal}>`]); + if (new Set(ports.map((p) => p.name)).size !== ports.length) + throw new Error(`Duplicate dependency name in ${entry.displayName}`); + const receiver = + entry.kind === "constructor" + ? ref({ kind: "atom", atomId: entry.constructsAtom }) + : entry.kind === "operation" && entry.receiverRequirement.kind === "exact-atom" + ? ref({ kind: "atom", atomId: entry.receiverRequirement.atomId }) + : entry.kind === "operation" && entry.receiverRequirement.kind === "all-interfaces" + ? `QxObjectRef<${entry.receiverRequirement.interfaceRevisionIds.map((id) => q(`interface:${id}`)).join(" | ") || "never"}>` + : "QxObjectRef"; + const contextShape = object([ + ["objectId", receiver], + ["input", type(entry.inputType)], + ["ports", object(ports.map((port) => [port.name, port.type]))], + ]); + contexts.push([ + entry.displayName, + `${contextShape} & QxContextLifecycle<${contextShape} & {signal?: AbortSignal}>`, + ]); const event = entry.kind === "operation" ? entry.eventType : undefined; const contextType = `Contexts[${q(entry.displayName)}]`; const outputType = type(event ?? entry.outputType); // Watch-start handlers produce events through the runtime's derived stream protocol. - handlers.push([entry.displayName, event ? `QxDerived<${contextType}, ${outputType}>` : - `QxHandler<${contextType}, ${outputType}>${entry.kind === "operation" && entry.mode === "call" ? ` | QxDerived<${contextType}, ${outputType}>` : ""}`]); - specs[entry.displayName] = { inputType: entry.inputType, outputType: entry.outputType, - ...(event ? { eventType: event } : {}), ports: Object.fromEntries(ports.map((port) => [port.name, port.spec])) }; + handlers.push([ + entry.displayName, + event + ? `QxDerived<${contextType}, ${outputType}>` + : `QxHandler<${contextType}, ${outputType}>${entry.kind === "operation" && entry.mode === "call" ? ` | QxDerived<${contextType}, ${outputType}>` : ""}`, + ]); + specs[entry.displayName] = { + inputType: entry.inputType, + outputType: entry.outputType, + ...(event ? { eventType: event } : {}), + ports: Object.fromEntries(ports.map((port) => [port.name, port.spec])), + }; } const imports = [...messages].map(([id, alias]) => { const binding = options.messages![id]!; - if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(binding.export)) throw new Error(`Invalid message binding export ${binding.export}`); + if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(binding.export)) + throw new Error(`Invalid message binding export ${binding.export}`); return `import { ${binding.export} as ${alias} } from ${q(binding.module)};`; }); const signatures = `${object(contexts)} ${object(handlers)}`; - const typeImports = ["BindingValue", "QxObjectRef", "QxWatchHandle", "QxHandler", "QxDerived", "QxContextLifecycle", "QxLiveValue", "RelationshipCollection", "RelationshipEntry"].filter((name) => new RegExp(`\\b${name}\\b`).test(signatures)); - return `// Generated by quixos-codegen-ts. Do not edit. Binding ABI version 1.\n` + + const typeImports = [ + "BindingValue", + "QxObjectRef", + "QxWatchHandle", + "QxHandler", + "QxDerived", + "QxContextLifecycle", + "QxLiveValue", + "RelationshipCollection", + "RelationshipEntry", + ].filter((name) => new RegExp(`\\b${name}\\b`).test(signatures)); + return ( + `// Generated by quixos-codegen-ts. Do not edit. Binding ABI version 1.\n` + `import { ${exports.length ? "bindQxHandler, " : ""}${[...typeImports, "QxHandlerSpec", "QxMessages"].map((name) => `type ${name}`).join(", ")} } from ${q(options.runtimeModule ?? "@quixos/camino-package-runtime")};\n` + - imports.join("\n") + `\nexport const packageRevisionId = ${q(pkg.revisionId)};\n` + + imports.join("\n") + + `\nexport const packageRevisionId = ${q(pkg.revisionId)};\n` + `export type Contexts = ${object(contexts)};\nexport type Implementation = ${object(handlers)};\n` + `const messages = { ${[...messages].map(([id, alias]) => `${q(id)}: ${alias}`).join(", ")} } satisfies QxMessages;\n` + `const specs = ${JSON.stringify(specs, null, 2)} satisfies Record;\n` + `export const createRuntime = (implementation: Implementation) => ({\n packageRevisionId,\n exports: {\n` + - exports.map((entry) => ` ${q(entry.id)}: bindQxHandler(specs[${q(entry.displayName)}], implementation[${q(entry.displayName)}], messages),`).join("\n") + - `\n },\n});\n`; + exports + .map( + (entry) => + ` ${q(entry.id)}: bindQxHandler(specs[${q(entry.displayName)}], implementation[${q(entry.displayName)}], messages),`, + ) + .join("\n") + + `\n },\n});\n` + ); }; diff --git a/src/capability-language/assembly.ts b/src/capability-language/assembly.ts index ecaf2e3..8a8fd91 100644 --- a/src/capability-language/assembly.ts +++ b/src/capability-language/assembly.ts @@ -64,11 +64,9 @@ export type CompiledCapabilityResourceRepository = { const sourceKey = (kind: LockedResource["kind"], source: GitSource) => `${kind}\0${source.repository}\0${source.commit.toLowerCase()}`; -const bindingKey = (kind: LockedResource["kind"], binding: string) => - `${kind}\0${binding}`; +const bindingKey = (kind: LockedResource["kind"], binding: string) => `${kind}\0${binding}`; -const importsKey = (entry: CapabilityResourceImport | LockedResource) => - bindingKey(entry.kind, entry.binding); +const importsKey = (entry: CapabilityResourceImport | LockedResource) => bindingKey(entry.kind, entry.binding); const assertImportsMatchLock = ( label: string, @@ -77,22 +75,23 @@ const assertImportsMatchLock = ( ) => { const authored = [...imports].map(importsKey).sort(); const locked = [...resources].map(importsKey).sort(); - if ( - authored.length !== locked.length || - authored.some((entry, index) => entry !== locked[index]) - ) { + if (authored.length !== locked.length || authored.some((entry, index) => entry !== locked[index])) { throw new Error( `${label} imports do not match quixos.lock:\n` + - `authored: ${authored.join(", ") || "none"}\n` + - `locked: ${locked.join(", ") || "none"}`, + `authored: ${authored.join(", ") || "none"}\n` + + `locked: ${locked.join(", ") || "none"}`, ); } }; -const exactRevisions = (revisions: readonly Revision[]) => { +const exactRevisions = < + Revision extends { + revisionId: string; + source: SourceRevision; + }, +>( + revisions: readonly Revision[], +) => { const seen = new Set(); return revisions.filter((revision) => { const key = `${revision.revisionId}\0${revision.source.repository}\0${revision.source.commit}`; @@ -122,19 +121,20 @@ const diagnosticsMessage = ( message: string; path?: string; }[], -) => `${label} did not compile:\n${diagnostics.map((entry) => { - const location = entry.line > 0 - ? `${entry.fileName}:${entry.line}:${entry.column + 1}` - : `${entry.fileName}${entry.path ? `:${entry.path}` : ""}`; - return `${location}: ${entry.phase} ${entry.code}: ${entry.message}`; -}).join("\n")}`; +) => + `${label} did not compile:\n${diagnostics + .map((entry) => { + const location = + entry.line > 0 + ? `${entry.fileName}:${entry.line}:${entry.column + 1}` + : `${entry.fileName}${entry.path ? `:${entry.path}` : ""}`; + return `${location}: ${entry.phase} ${entry.code}: ${entry.message}`; + }) + .join("\n")}`; -const revisionFor = (node: ResolvedCapabilityResource) => - node.resource.revision; +const revisionFor = (node: ResolvedCapabilityResource) => node.resource.revision; -const resourceClosure = ( - roots: readonly ResolvedCapabilityResource[], -): ResolvedCapabilityResource[] => { +const resourceClosure = (roots: readonly ResolvedCapabilityResource[]): ResolvedCapabilityResource[] => { const result: ResolvedCapabilityResource[] = []; const seen = new Set(); const visit = (node: ResolvedCapabilityResource) => { @@ -151,25 +151,26 @@ const environmentFor = ( direct: readonly [LockedResource, ResolvedCapabilityResource][], closure: readonly ResolvedCapabilityResource[], ): CapabilityImportEnvironment => ({ - interfaces: new Map(direct.flatMap(([locked, node]) => - node.resource.kind === "interface" - ? [[locked.binding, node.resource.revision] as const] - : [])), - packages: new Map(direct.flatMap(([locked, node]) => - node.resource.kind === "package" - ? [[locked.binding, node.resource.revision] as const] - : [])), - interfaceClosure: exactRevisions(closure.flatMap((node) => - node.resource.kind === "interface" ? [node.resource.revision] : [])), - packageClosure: exactRevisions(closure.flatMap((node) => - node.resource.kind === "package" ? [node.resource.revision] : [])), + interfaces: new Map( + direct.flatMap(([locked, node]) => + node.resource.kind === "interface" ? [[locked.binding, node.resource.revision] as const] : [], + ), + ), + packages: new Map( + direct.flatMap(([locked, node]) => + node.resource.kind === "package" ? [[locked.binding, node.resource.revision] as const] : [], + ), + ), + interfaceClosure: exactRevisions( + closure.flatMap((node) => (node.resource.kind === "interface" ? [node.resource.revision] : [])), + ), + packageClosure: exactRevisions( + closure.flatMap((node) => (node.resource.kind === "package" ? [node.resource.revision] : [])), + ), externalAtoms: exactAtoms(closure.flatMap((node) => node.resource.externalAtoms)), }); -const createResourceGraphResolver = ( - quixosCommit: string, - resolveResource: CapabilityRepositoryResolver, -) => { +const createResourceGraphResolver = (quixosCommit: string, resolveResource: CapabilityRepositoryResolver) => { const resolved = new Map>(); const active: string[] = []; @@ -188,7 +189,7 @@ const createResourceGraphResolver = ( const pending = (async () => { active.push(key); try { - const snapshot = suppliedSnapshot ?? await resolveResource(locked.source, locked.kind); + const snapshot = suppliedSnapshot ?? (await resolveResource(locked.source, locked.kind)); const lockResult = await loadQuixosLock(path.join(snapshot.directory, "quixos.lock")); if (!lockResult.ok) { throw new Error(diagnosticsMessage(`${locked.kind} ${locked.binding} lock`, lockResult.diagnostics)); @@ -196,22 +197,20 @@ const createResourceGraphResolver = ( if (lockResult.lock.quixos.policy) { throw new Error( `${locked.kind} ${locked.binding} lock declares workspace Quixos policy ` + - `${lockResult.lock.quixos.policy}; resource locks may only declare their exact authored-against commit`, + `${lockResult.lock.quixos.policy}; resource locks may only declare their exact authored-against commit`, ); } if (lockResult.lock.quixos.commit.toLowerCase() !== quixosCommit.toLowerCase()) { throw new Error( `${locked.kind} ${locked.binding} selects Quixos ${lockResult.lock.quixos.commit}, ` + - `but the repository graph selects ${quixosCommit}`, + `but the repository graph selects ${quixosCommit}`, ); } const directPairs: Array<[LockedResource, ResolvedCapabilityResource]> = []; for (const dependency of lockResult.lock.resources) { directPairs.push([dependency, await visit(dependency)]); } - const dependencyClosure = resourceClosure( - directPairs.map(([, node]) => node), - ); + const dependencyClosure = resourceClosure(directPairs.map(([, node]) => node)); const environment = environmentFor(directPairs, dependencyClosure); const manifestName = locked.kind === "interface" ? "interface.qx" : "package.qx"; const manifestPath = path.join(snapshot.directory, manifestName); @@ -225,22 +224,28 @@ const createResourceGraphResolver = ( throw new Error(diagnosticsMessage(`${locked.kind} ${locked.binding}`, compiled.diagnostics)); } if (compiled.resource.kind !== locked.kind) { - throw new Error( - `${manifestPath} declares ${compiled.resource.kind}, not ${locked.kind}`, - ); + throw new Error(`${manifestPath} declares ${compiled.resource.kind}, not ${locked.kind}`); } assertImportsMatchLock(manifestPath, compiled.resource.imports, lockResult.lock.resources); if (compiled.resource.kind === "package") { let catalogText: string | undefined; - try { catalogText = await readFile(path.join(snapshot.directory, "quixos.migrations.json"), "utf8"); } - catch (error) { if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; } + try { + catalogText = await readFile(path.join(snapshot.directory, "quixos.migrations.json"), "utf8"); + } catch (error) { + if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; + } if (catalogText !== undefined) { - const catalog = validateMigrationCatalog(JSON.parse(catalogText), new Set(compiled.resource.revision.exports.map((entry) => entry.id))); + const catalog = validateMigrationCatalog( + JSON.parse(catalogText), + new Set(compiled.resource.revision.exports.map((entry) => entry.id)), + ); const root = await realpath(snapshot.directory); for (const migration of catalog.migrations) { const implementation = await realpath(path.join(root, migration.implementation.file)); - if (!implementation.startsWith(`${root}${path.sep}`)) throw new Error("Migration implementation escapes its package"); - if (contentDigest(await readFile(implementation, "utf8")) !== migration.implementation.digest) throw new Error(`Migration implementation digest mismatch: ${migration.id}`); + if (!implementation.startsWith(`${root}${path.sep}`)) + throw new Error("Migration implementation escapes its package"); + if (contentDigest(await readFile(implementation, "utf8")) !== migration.implementation.digest) + throw new Error(`Migration implementation digest mismatch: ${migration.id}`); } compiled.resource.revision.migrationCatalog = catalog; } @@ -252,10 +257,9 @@ const createResourceGraphResolver = ( directory: snapshot.directory, lock: lockResult.lock, resource: compiled.resource, - dependencies: new Map(directPairs.map(([dependency, node]) => [ - bindingKey(dependency.kind, dependency.binding), - node, - ])), + dependencies: new Map( + directPairs.map(([dependency, node]) => [bindingKey(dependency.kind, dependency.binding), node]), + ), }; } finally { active.pop(); @@ -281,15 +285,15 @@ export const compileCapabilityResourceRepository = async (options: { if (!lockResult.ok) { throw new Error(diagnosticsMessage("Resource lock", lockResult.diagnostics)); } - const resolver = createResourceGraphResolver( - lockResult.lock.quixos.commit, - options.resolveResource, + const resolver = createResourceGraphResolver(lockResult.lock.quixos.commit, options.resolveResource); + const root = await resolver.visit( + { + kind: options.kind, + binding: "", + source: options.source, + }, + { directory: options.rootDirectory }, ); - const root = await resolver.visit({ - kind: options.kind, - binding: "", - source: options.source, - }, { directory: options.rootDirectory }); return { resource: root.resource, lock: root.lock, @@ -307,9 +311,7 @@ export const compileWorkspaceRepository = async (options: { /** An editor's proposed source snapshot; locks and dependency revisions remain exact. */ readSource?: (name: string) => Promise; }): Promise => { - const rootLockResult = await loadQuixosLock( - path.join(options.rootDirectory, "quixos.lock"), - ); + const rootLockResult = await loadQuixosLock(path.join(options.rootDirectory, "quixos.lock")); if (!rootLockResult.ok) { throw new Error(diagnosticsMessage("Workspace lock", rootLockResult.diagnostics)); } @@ -323,17 +325,25 @@ export const compileWorkspaceRepository = async (options: { const nodes = await resolver.nodes(); const environment = environmentFor(directPairs, nodes); const workspacePath = path.join(options.rootDirectory, "workspace.qx"); - const sources = options.readSource ? await resolveQxSources(options.readSource) : await loadQxSources(options.rootDirectory); + const sources = options.readSource + ? await resolveQxSources(options.readSource) + : await loadQxSources(options.rootDirectory); const compiled = compileCapabilitySource(sources.source, workspacePath, environment); if (!compiled.ok) { - throw new Error(diagnosticsMessage("Workspace", compiled.diagnostics.map((diagnostic) => - diagnostic.line > 0 ? { ...diagnostic, ...sources.originalPosition(diagnostic.line, diagnostic.column) } : diagnostic))); + throw new Error( + diagnosticsMessage( + "Workspace", + compiled.diagnostics.map((diagnostic) => + diagnostic.line > 0 + ? { ...diagnostic, ...sources.originalPosition(diagnostic.line, diagnostic.column) } + : diagnostic, + ), + ), + ); } assertImportsMatchLock(workspacePath, compiled.imports, rootLock.resources); const availableInterfaceIds = new Set( - nodes.flatMap((node) => node.resource.kind === "interface" - ? [node.resource.revision.revisionId] - : []), + nodes.flatMap((node) => (node.resource.kind === "interface" ? [node.resource.revision.revisionId] : [])), ); const availableAtomIds = new Set(compiled.workspace.atoms.map((atom) => atom.id)); for (const node of nodes) { @@ -341,7 +351,7 @@ export const compileWorkspaceRepository = async (options: { if (!availableInterfaceIds.has(requirement.revisionId)) { throw new Error( `${node.kind} ${node.resource.revision.displayName} requires external interface ` + - `${requirement.binding} (${requirement.revisionId}), but the workspace resource graph does not provide it`, + `${requirement.binding} (${requirement.revisionId}), but the workspace resource graph does not provide it`, ); } } @@ -349,28 +359,31 @@ export const compileWorkspaceRepository = async (options: { if (!availableAtomIds.has(atom.id)) { throw new Error( `${node.kind} ${node.resource.revision.displayName} requires external atom ` + - `${atom.displayName} (${atom.id}), but the workspace does not define it`, + `${atom.displayName} (${atom.id}), but the workspace does not define it`, ); } } } const workspace: WorkspaceRevision = { ...compiled.workspace, - ...(options.workspaceId - ? { workspaceId: capabilityId.workspace(options.workspaceId) } - : {}), + ...(options.workspaceId ? { workspaceId: capabilityId.workspace(options.workspaceId) } : {}), ...(options.workspaceRevisionId ? { id: capabilityId.workspaceRevision(options.workspaceRevisionId) } - : options.sourceRootCommit ? { id: capabilityId.workspaceRevision(`workspace-revision:${options.workspaceId ?? compiled.workspace.workspaceId}:${options.sourceRootCommit}`) } : {}), - ...(options.sourceRootCommit - ? { sourceRootCommit: options.sourceRootCommit } - : {}), + : options.sourceRootCommit + ? { + id: capabilityId.workspaceRevision( + `workspace-revision:${options.workspaceId ?? compiled.workspace.workspaceId}:${options.sourceRootCommit}`, + ), + } + : {}), + ...(options.sourceRootCommit ? { sourceRootCommit: options.sourceRootCommit } : {}), }; const checked = compileWorkspaceRevision(workspace); if (!checked.ok) { throw new Error( - `Instantiated workspace did not compile:\n${checked.issues.map((entry) => - `${entry.path}: ${entry.message}`).join("\n")}`, + `Instantiated workspace did not compile:\n${checked.issues + .map((entry) => `${entry.path}: ${entry.message}`) + .join("\n")}`, ); } return { @@ -378,9 +391,6 @@ export const compileWorkspaceRepository = async (options: { plan: checked.plan, lock: rootLock, resources: nodes, - directResources: new Map(directPairs.map(([locked, node]) => [ - bindingKey(locked.kind, locked.binding), - node, - ])), + directResources: new Map(directPairs.map(([locked, node]) => [bindingKey(locked.kind, locked.binding), node])), }; }; diff --git a/src/capability-language/authoring-check.ts b/src/capability-language/authoring-check.ts index 023929e..eb0a94a 100644 --- a/src/capability-language/authoring-check.ts +++ b/src/capability-language/authoring-check.ts @@ -1,5 +1,5 @@ import fs from "node:fs/promises"; -import {appendFileSync} from "node:fs"; +import { appendFileSync } from "node:fs"; import path from "node:path"; import { createHash, randomUUID } from "node:crypto"; import { authoringContext } from "./authoring-context.js"; @@ -10,21 +10,45 @@ import { buildImmutableCandidate, checkerIdentity } from "./checked-build.js"; import { planEvolution, type WorkspaceRevision, type EvolutionReview } from "../capability-model/index.js"; export const checkRecordName = (directory: string) => createHash("sha256").update(directory).digest("hex") + ".json"; -export async function checkAuthoring(start: string, output: string, options: { baseline?: string; reviews?: string; contractOnly?: boolean } = {}) { +export async function checkAuthoring( + start: string, + output: string, + options: { baseline?: string; reviews?: string; contractOnly?: boolean } = {}, +) { const started = performance.now(); const timings: Record = {}; const context = await authoringContext(start); const location = await fs.realpath(start); const directory = location === context.workbench ? "root" : path.relative(context.workbench, location); - const resource = context.resources.find(entry => entry.directory === directory); + const resource = context.resources.find((entry) => entry.directory === directory); if (!resource) throw new Error("Run check from a registered repository root or the workbench"); await fs.mkdir(output, { mode: 0o700 }); - const report: { directory: string; checker: string; candidateOnly: true; activationEvidence: false; commit?: string; artifactPath?: string; blockers: string[]; phase: string; output: string; compilation?: "passed"; activationReadiness?: "preserve" | "migration-required" | "blocked"; migrationRequired?: string[] } = { - directory, checker: checkerIdentity(), candidateOnly: true, activationEvidence: false, blockers: [], phase: "convergence", output, + const report: { + directory: string; + checker: string; + candidateOnly: true; + activationEvidence: false; + commit?: string; + artifactPath?: string; + blockers: string[]; + phase: string; + output: string; + compilation?: "passed"; + activationReadiness?: "preserve" | "migration-required" | "blocked"; + migrationRequired?: string[]; + } = { + directory, + checker: checkerIdentity(), + candidateOnly: true, + activationEvidence: false, + blockers: [], + phase: "convergence", + output, }; const progress = async (running = true) => { - const file = path.join(output, "report.json"), temp = `${file}.tmp`; - await fs.writeFile(temp, JSON.stringify({...report, timings, running}, null, 2)); + const file = path.join(output, "report.json"), + temp = `${file}.tmp`; + await fs.writeFile(temp, JSON.stringify({ ...report, timings, running }, null, 2)); await fs.rename(temp, file); }; await progress(); @@ -33,31 +57,43 @@ export async function checkAuthoring(start: string, output: string, options: { b // Serialize only source capture, not the potentially slow Nix build. // Repository-scoped agents can check separate immutable candidates in parallel. const capture = promisify(callback)("quixos-qx", ["converge", context.workbench, directory], { - maxBuffer: 4 * 1024 * 1024, env: {...process.env, QUIXOS_JJ_NO_CHECKPOINT: "1", QUIXOS_TRACE_CAPTURE: "1"}, + maxBuffer: 4 * 1024 * 1024, + env: { ...process.env, QUIXOS_JJ_NO_CHECKPOINT: "1", QUIXOS_TRACE_CAPTURE: "1" }, }); console.error(`Capture details: ${path.join(output, "capture.log")}`); - capture.child.stderr?.on("data", chunk => appendFileSync(path.join(output, "capture.log"), chunk)); - const captured = await capture.catch(error => { - if (typeof error.stdout === "string" && error.stdout.trim().startsWith("{")) return {stdout: error.stdout}; + capture.child.stderr?.on("data", (chunk) => appendFileSync(path.join(output, "capture.log"), chunk)); + const captured = await capture.catch((error) => { + if (typeof error.stdout === "string" && error.stdout.trim().startsWith("{")) return { stdout: error.stdout }; throw error; }); const converged = JSON.parse(captured.stdout) as Awaited>; timings.captureMs = Math.round(performance.now() - started); console.error(`[${new Date().toISOString()}] Check: source captured in ${(timings.captureMs / 1000).toFixed(1)}s`); if (!converged.candidate) { - report.phase = converged.worklist.find(entry => entry.phase !== "dependency")?.phase ?? "convergence"; - throw new Error(converged.worklist.map(entry => `${entry.directory} [${entry.phase}]: ${entry.message}`).join("\n")); + report.phase = converged.worklist.find((entry) => entry.phase !== "dependency")?.phase ?? "convergence"; + throw new Error( + converged.worklist.map((entry) => `${entry.directory} [${entry.phase}]: ${entry.message}`).join("\n"), + ); } report.commit = converged.candidate.commit; report.phase = "verification"; await progress(); const buildStarted = performance.now(); - console.error(`[${new Date().toISOString()}] Check: immutable Nix ${options.contractOnly ? "contract" : "verification"}; build output: ${path.join(output, "nix.log")}`); + console.error( + `[${new Date().toISOString()}] Check: immutable Nix ${options.contractOnly ? "contract" : "verification"}; build output: ${path.join(output, "nix.log")}`, + ); try { - report.artifactPath = await buildImmutableCandidate(converged.candidate, resource.kind, path.join(output, "nix.log"), options.contractOnly); + report.artifactPath = await buildImmutableCandidate( + converged.candidate, + resource.kind, + path.join(output, "nix.log"), + options.contractOnly, + ); } finally { timings.immutableCheckMs = Math.round(performance.now() - buildStarted); - console.error(`[${new Date().toISOString()}] Check: immutable phase ended after ${(timings.immutableCheckMs / 1000).toFixed(1)}s`); + console.error( + `[${new Date().toISOString()}] Check: immutable phase ended after ${(timings.immutableCheckMs / 1000).toFixed(1)}s`, + ); } const candidateText = await fs.readFile(path.join(report.artifactPath, "candidate.json"), "utf8"); await fs.writeFile(path.join(output, "candidate.json"), candidateText); @@ -69,36 +105,62 @@ export async function checkAuthoring(start: string, output: string, options: { b if (!baseline) { try { const host = JSON.parse(await fs.readFile("/etc/quixos/workspace-source.json", "utf8")); - if (await fs.realpath(host.workbenchRoot) === context.workbench) baseline = JSON.parse(await fs.readFile(path.join(host.runtimeClosureRoot, "manifest.json"), "utf8")).workspacePlanPath; - } catch (error) { if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; } + if ((await fs.realpath(host.workbenchRoot)) === context.workbench) + baseline = JSON.parse( + await fs.readFile(path.join(host.runtimeClosureRoot, "manifest.json"), "utf8"), + ).workspacePlanPath; + } catch (error) { + if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; + } } - const before = baseline ? JSON.parse(await fs.readFile(baseline, "utf8")) as WorkspaceRevision : null; - const reviews = options.reviews ? JSON.parse(await fs.readFile(options.reviews, "utf8")) as EvolutionReview[] : []; + const before = baseline ? (JSON.parse(await fs.readFile(baseline, "utf8")) as WorkspaceRevision) : null; + const reviews = options.reviews + ? (JSON.parse(await fs.readFile(options.reviews, "utf8")) as EvolutionReview[]) + : []; const evolution = planEvolution(before, JSON.parse(candidateText), { reviews }); await fs.writeFile(path.join(output, "evolution.json"), JSON.stringify(evolution, null, 2)); report.blockers.push(...evolution.blockers); report.migrationRequired = evolution.migrationRequired; - report.activationReadiness = evolution.blockers.length ? "blocked" : evolution.migrationRequired.length ? "migration-required" : "preserve"; - if (evolution.migrationRequired.length) report.blockers.push(`Explicit migration required for: ${evolution.migrationRequired.join(", ")}. Compilation passed; supply a migration path before cutover.`); + report.activationReadiness = evolution.blockers.length + ? "blocked" + : evolution.migrationRequired.length + ? "migration-required" + : "preserve"; + if (evolution.migrationRequired.length) + report.blockers.push( + `Explicit migration required for: ${evolution.migrationRequired.join(", ")}. Compilation passed; supply a migration path before cutover.`, + ); } if (!report.blockers.length) report.phase = options.contractOnly ? "contract-only" : "checked"; - } catch (error) { report.blockers.push(String(error instanceof Error ? error.message : error)); } + } catch (error) { + report.blockers.push(String(error instanceof Error ? error.message : error)); + } timings.totalMs = Math.round(performance.now() - started); - Object.assign(report, {timings}); + Object.assign(report, { timings }); await progress(false); if (options.contractOnly) return report; const records = path.join(context.workbench, ".quixos/checks"); await fs.mkdir(records, { recursive: true }); const remember = async (value: typeof report) => { - const filename = path.join(records, checkRecordName(value.directory)), temporary = `${filename}.${randomUUID()}.tmp`; + const filename = path.join(records, checkRecordName(value.directory)), + temporary = `${filename}.${randomUUID()}.tmp`; await fs.writeFile(temporary, JSON.stringify(value, null, 2), { flag: "wx", mode: 0o600 }); await fs.rename(temporary, filename); }; if (report.artifactPath) { const graph = JSON.parse(await fs.readFile(path.join(report.artifactPath, "graph.json"), "utf8")); for (const checked of graph.resources) { - const managed = context.resources.find(entry => entry.kind === checked.kind && entry.source?.repository === checked.source.repository); - if (managed && managed.directory !== directory) await remember({...report, directory: managed.directory, commit: checked.source.commit, blockers: [], phase: "checked"}); + const managed = context.resources.find( + (entry) => entry.kind === checked.kind && entry.source?.repository === checked.source.repository, + ); + if (managed && managed.directory !== directory) + await remember({ + ...report, + directory: managed.directory, + commit: checked.source.commit, + blockers: [], + phase: "checked", + }); } } await remember(report); diff --git a/src/capability-language/authoring-context.ts b/src/capability-language/authoring-context.ts index 09d7dfd..f91a318 100644 --- a/src/capability-language/authoring-context.ts +++ b/src/capability-language/authoring-context.ts @@ -14,33 +14,54 @@ export async function authoringContext(start: string) { let workbench = await realpath(start); for (;;) { let text: string | undefined; - try { text = await readFile(path.join(workbench, ".quixos/resource-graph.json"), "utf8"); } - catch (error) { if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; } + try { + text = await readFile(path.join(workbench, ".quixos/resource-graph.json"), "utf8"); + } catch (error) { + if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; + } if (text !== undefined) { const graph = JSON.parse(text) as { resources: AuthoringResource[] }; if (!Array.isArray(graph.resources)) throw new Error("Managed resource inventory is malformed"); const resources: AuthoringResource[] = [{ kind: "workspace", directory: "root" }]; - const identities = new Set(), directories = new Set(["root"]); + const identities = new Set(), + directories = new Set(["root"]); for (const entry of graph.resources) { // Compiler graphs carry resolved paths; the authoring API presents // stable workbench-relative names and validates containment here. - if (typeof entry.directory === "string" && path.isAbsolute(entry.directory)) entry.directory = path.relative(workbench, entry.directory); - if (!["interface", "package"].includes(entry.kind) || !entry.source || - !/^resources\/[A-Za-z0-9][A-Za-z0-9._-]*$/.test(entry.directory)) { + if (typeof entry.directory === "string" && path.isAbsolute(entry.directory)) + entry.directory = path.relative(workbench, entry.directory); + if ( + !["interface", "package"].includes(entry.kind) || + !entry.source || + !/^resources\/[A-Za-z0-9][A-Za-z0-9._-]*$/.test(entry.directory) + ) { throw new Error("Invalid managed resource registration"); } const identity = `${entry.kind}\0${entry.resourceId ?? entry.source.repository}`; if (identities.has(identity) || directories.has(entry.directory)) { throw new Error(`Multiple editable selections for ${entry.resourceId ?? entry.source.repository}`); } - identities.add(identity); directories.add(entry.directory); - resources.push({kind: entry.kind, directory: entry.directory, resourceId: entry.resourceId, source: entry.source}); + identities.add(identity); + directories.add(entry.directory); + resources.push({ + kind: entry.kind, + directory: entry.directory, + resourceId: entry.resourceId, + source: entry.source, + }); } - return { workbench, resources, async baseline() { - const result = await loadQuixosLock(path.join(workbench, "root/quixos.lock")); - if (!result.ok) throw new Error(`Workspace source baseline is invalid: ${result.diagnostics.map(d => d.message).join("; ")}`); - return result.lock.quixos; - } }; + return { + workbench, + resources, + async baseline() { + const result = await loadQuixosLock(path.join(workbench, "root/quixos.lock")); + if (!result.ok) + throw new Error( + `Workspace source baseline is invalid: ${result.diagnostics.map((d) => d.message).join("; ")}`, + ); + return result.lock.quixos; + }, + }; } const parent = path.dirname(workbench); if (parent === workbench) throw new Error("Not in a managed workbench; select one with --workbench DIRECTORY"); diff --git a/src/capability-language/authoring-converge.ts b/src/capability-language/authoring-converge.ts index 200fd32..c410152 100644 --- a/src/capability-language/authoring-converge.ts +++ b/src/capability-language/authoring-converge.ts @@ -5,7 +5,13 @@ import { promisify } from "node:util"; import { randomUUID } from "node:crypto"; import { authoringContext } from "./authoring-context.js"; import { snapshotCommit } from "./checked-build.js"; -import { loadQuixosLock, parseQuixosLockDocument, formatQuixosLockDocument, retentionTagForCommit, type GitSource } from "../resource-lock/index.js"; +import { + loadQuixosLock, + parseQuixosLockDocument, + formatQuixosLockDocument, + retentionTagForCommit, + type GitSource, +} from "../resource-lock/index.js"; const execFile = promisify(callback); const command = async (cwd: string, executable: string, args: string[]) => { @@ -13,15 +19,26 @@ const command = async (cwd: string, executable: string, args: string[]) => { // Do not log arguments: transports may contain credentials. Source identities // remain in the normal checked result, not in this timing channel. const label = `${path.basename(cwd)} ${executable} ${args[0]}`; - try { return (await execFile(executable, args, { - cwd, maxBuffer: 4 * 1024 * 1024, - env: { ...process.env, QUIXOS_JJ_NO_CHECKPOINT: "1", QUIXOS_SUBTREE_PUBLISH: "0", GIT_TERMINAL_PROMPT: "0" }, - })).stdout.trim(); } - finally { if (process.env.QUIXOS_TRACE_CAPTURE === "1") console.error(`[${new Date().toISOString()}] Capture: ${label}: ${Math.round(performance.now() - start)}ms`); } + try { + return ( + await execFile(executable, args, { + cwd, + maxBuffer: 4 * 1024 * 1024, + env: { ...process.env, QUIXOS_JJ_NO_CHECKPOINT: "1", QUIXOS_SUBTREE_PUBLISH: "0", GIT_TERMINAL_PROMPT: "0" }, + }) + ).stdout.trim(); + } finally { + if (process.env.QUIXOS_TRACE_CAPTURE === "1") + console.error(`[${new Date().toISOString()}] Capture: ${label}: ${Math.round(performance.now() - start)}ms`); + } }; const identity = (kind: string, repository: string) => `${kind}\0${repository}`; -export type AuthoringBlocker = { directory: string; phase: "resolution" | "dependency" | "source" | "publication" | "concurrent-edit"; message: string }; +export type AuthoringBlocker = { + directory: string; + phase: "resolution" | "dependency" | "source" | "publication" | "concurrent-edit"; + message: string; +}; /** Source retention only. Neither successful convergence nor an empty source * worklist grants typechecking, semantic review or activation approval. @@ -37,94 +54,136 @@ export async function convergeAuthoring(start: string, target = "root") { const root = path.join(context.workbench, entry.directory); let repository = entry.source?.repository; try { - if (await realpath(root) !== root) throw new Error(`Managed checkout crosses a symlink: ${entry.directory}`); + if ((await realpath(root)) !== root) throw new Error(`Managed checkout crosses a symlink: ${entry.directory}`); // Transport rewrites must not become committed source identities. const origin = await command(root, "git", ["config", "--get", "remote.origin.url"]); - if (repository && origin !== repository) throw new Error(`Origin differs from registered source for ${entry.directory}`); + if (repository && origin !== repository) + throw new Error(`Origin differs from registered source for ${entry.directory}`); repository ??= origin; } catch (error) { - blockers.push({directory: entry.directory, phase: "source", message: String(error).slice(0, 2000)}); + blockers.push({ directory: entry.directory, phase: "source", message: String(error).slice(0, 2000) }); if (!repository) throw error; // The root has no separate registered source. } const key = identity(entry.kind, repository); if (selected.has(key)) throw new Error(`More than one editable checkout for ${repository}`); selected.set(key, entry.directory); - nodes.set(entry.directory, { ...entry, source: { resolver: "git", repository, commit: entry.source?.commit ?? "" }, dependencies: [] }); + nodes.set(entry.directory, { + ...entry, + source: { resolver: "git", repository, commit: entry.source?.commit ?? "" }, + dependencies: [], + }); } for (const node of nodes.values()) { try { const lock = await loadQuixosLock(path.join(context.workbench, node.directory, "quixos.lock")); - if (!lock.ok) throw new Error(lock.diagnostics.map(d => `${d.fileName}: ${d.message}`).join("\n")); - node.dependencies = [...new Set(lock.lock.resources.flatMap(entry => { - const directory = selected.get(identity(entry.kind, entry.source.repository)); - return directory ? [directory] : []; - }))]; - } catch (error) { blockers.push({ directory: node.directory, phase: "resolution", message: String(error) }); } + if (!lock.ok) throw new Error(lock.diagnostics.map((d) => `${d.fileName}: ${d.message}`).join("\n")); + node.dependencies = [ + ...new Set( + lock.lock.resources.flatMap((entry) => { + const directory = selected.get(identity(entry.kind, entry.source.repository)); + return directory ? [directory] : []; + }), + ), + ]; + } catch (error) { + blockers.push({ directory: node.directory, phase: "resolution", message: String(error) }); + } } - const complete = new Map(), active = new Set(); + const complete = new Map(), + active = new Set(); const visited = new Set(); if (!nodes.has(target)) throw new Error(`Not a registered repository: ${target}`); const visit = async (directory: string): Promise => { visited.add(directory); if (complete.has(directory)) return true; - if (blockers.some(entry => entry.directory === directory)) return false; - if (active.has(directory)) { blockers.push({ directory, phase: "dependency", message: `Source dependency cycle: ${[...active, directory].join(" -> ")}` }); return false; } + if (blockers.some((entry) => entry.directory === directory)) return false; + if (active.has(directory)) { + blockers.push({ + directory, + phase: "dependency", + message: `Source dependency cycle: ${[...active, directory].join(" -> ")}`, + }); + return false; + } active.add(directory); const node = nodes.get(directory)!; - for (const dependency of node.dependencies) if (!await visit(dependency)) { - blockers.push({ directory, phase: "dependency", message: `Waiting for ${dependency}` }); active.delete(directory); return false; - } + for (const dependency of node.dependencies) + if (!(await visit(dependency))) { + blockers.push({ directory, phase: "dependency", message: `Waiting for ${dependency}` }); + active.delete(directory); + return false; + } const root = path.join(context.workbench, directory); let phase: AuthoringBlocker["phase"] = "source"; try { const lock = await loadQuixosLock(path.join(root, "quixos.lock")); if (!lock.ok) throw new Error("Lock changed during convergence; retry after joining writers"); for (const file of lock.lock.sourceFiles ?? ["quixos.lock"]) { - const filename = path.join(root, file), before = await readFile(filename, "utf8"); + const filename = path.join(root, file), + before = await readFile(filename, "utf8"); const parsed = parseQuixosLockDocument(before, file); if (!parsed.ok) throw new Error(`Invalid lock ${file}`); let changed = false; for (const dependency of parsed.document.resources) { const target = selected.get(identity(dependency.kind, dependency.source.repository)); const source = target ? complete.get(target) : undefined; - if (target && !source) throw new Error(`Dependencies changed during convergence (${dependency.binding}); join writers and retry`); - if (source && source.commit !== dependency.source.commit) { dependency.source = source; changed = true; } + if (target && !source) + throw new Error(`Dependencies changed during convergence (${dependency.binding}); join writers and retry`); + if (source && source.commit !== dependency.source.commit) { + dependency.source = source; + changed = true; + } } if (changed) { const temporary = `${filename}.${randomUUID()}.tmp`; try { await writeFile(temporary, formatQuixosLockDocument(parsed.document), { flag: "wx" }); - if (await readFile(filename, "utf8") !== before) throw new Error(`Concurrent edit to ${file}; retry after joining writers`); + if ((await readFile(filename, "utf8")) !== before) + throw new Error(`Concurrent edit to ${file}; retry after joining writers`); await rename(temporary, filename); - } finally { await rm(temporary, { force: true }); } + } finally { + await rm(temporary, { force: true }); + } } } const snapshotStarted = performance.now(); const commit = await snapshotCommit(root); - if (process.env.QUIXOS_TRACE_CAPTURE === "1") console.error(`[${new Date().toISOString()}] Capture: ${directory} snapshot: ${Math.round(performance.now() - snapshotStarted)}ms`); + if (process.env.QUIXOS_TRACE_CAPTURE === "1") + console.error( + `[${new Date().toISOString()}] Capture: ${directory} snapshot: ${Math.round(performance.now() - snapshotStarted)}ms`, + ); phase = "publication"; const ref = retentionTagForCommit(commit); const remote = await command(root, "git", ["ls-remote", "--refs", node.source.repository, ref]); if (remote && remote.split(/\s+/)[0] !== commit) throw new Error(`Conflicting immutable retention ref ${ref}`); if (!remote) await command(root, "git", ["push", node.source.repository, `${commit}:${ref}`]); - if ((await command(root, "git", ["ls-remote", "--refs", node.source.repository, ref])).split(/\s+/)[0] !== commit) throw new Error("Published source retention was not observed"); + if ((await command(root, "git", ["ls-remote", "--refs", node.source.repository, ref])).split(/\s+/)[0] !== commit) + throw new Error("Published source retention was not observed"); complete.set(directory, { ...node.source, commit }); - } catch (error) { blockers.push({ directory, phase, message: String(error).slice(0, 4000) }); } + } catch (error) { + blockers.push({ directory, phase, message: String(error).slice(0, 4000) }); + } active.delete(directory); return complete.has(directory); }; // Include newly created, not-yet-imported resources, then the root. - if (target === "root") for (const directory of [...nodes.keys()].filter(d => d !== "root")) await visit(directory); + if (target === "root") for (const directory of [...nodes.keys()].filter((d) => d !== "root")) await visit(directory); await visit(target); - for (let index = blockers.length - 1; index >= 0; index--) if (!visited.has(blockers[index].directory)) blockers.splice(index, 1); + for (let index = blockers.length - 1; index >= 0; index--) + if (!visited.has(blockers[index].directory)) blockers.splice(index, 1); for (const [directory, source] of complete) { - try { if (await snapshotCommit(path.join(context.workbench, directory)) !== source.commit) throw new Error("Source advanced while converging; join writers and retry"); } - catch (error) { blockers.push({ directory, phase: "concurrent-edit", message: String(error) }); } + try { + if ((await snapshotCommit(path.join(context.workbench, directory))) !== source.commit) + throw new Error("Source advanced while converging; join writers and retry"); + } catch (error) { + blockers.push({ directory, phase: "concurrent-edit", message: String(error) }); + } } // Persist successful selections even if another repository is still broken. // Recovery must not depend on all parents succeeding in the same invocation. const graph = JSON.parse(graphBefore); - for (const resource of graph.resources) resource.directory = path.relative(context.workbench, path.resolve(context.workbench, resource.directory)); + for (const resource of graph.resources) + resource.directory = path.relative(context.workbench, path.resolve(context.workbench, resource.directory)); const replacements = new Map(); for (const resource of graph.resources) { const source = complete.get(resource.directory); @@ -132,30 +191,36 @@ export async function convergeAuthoring(start: string, target = "root") { const key = `${resource.kind}\0${source.repository}\0${source.commit}`; replacements.set(resource.key, key); if (resource.source.commit !== source.commit) delete resource.revisionId; - resource.source = source; resource.key = key; + resource.source = source; + resource.key = key; } - for (const resource of graph.resources) for (const dependency of resource.dependencies ?? []) { - dependency.resourceKey = replacements.get(dependency.resourceKey) ?? dependency.resourceKey; - } - for (const direct of graph.directResources ?? []) direct.resourceKey = replacements.get(direct.resourceKey) ?? direct.resourceKey; + for (const resource of graph.resources) + for (const dependency of resource.dependencies ?? []) { + dependency.resourceKey = replacements.get(dependency.resourceKey) ?? dependency.resourceKey; + } + for (const direct of graph.directResources ?? []) + direct.resourceKey = replacements.get(direct.resourceKey) ?? direct.resourceKey; // Inventory is a projection of actual locks, including newly added/removed // imports. Never require a successful parent compilation to repair it. for (const [directory] of complete) { const lock = await loadQuixosLock(path.join(context.workbench, directory, "quixos.lock")); if (!lock.ok) continue; - const dependencies = lock.lock.resources.map(dependency => ({ + const dependencies = lock.lock.resources.map((dependency) => ({ binding: `${dependency.kind}\0${dependency.binding}`, resourceKey: `${dependency.kind}\0${dependency.source.repository}\0${dependency.source.commit}`, })); if (directory === "root") { graph.quixos = lock.lock.quixos; graph.directResources = lock.lock.resources.map((dependency, index) => ({ - kind: dependency.kind, binding: dependency.binding, resourceKey: dependencies[index].resourceKey, + kind: dependency.kind, + binding: dependency.binding, + resourceKey: dependencies[index].resourceKey, ...(selected.has(identity(dependency.kind, dependency.source.repository)) - ? {directory: selected.get(identity(dependency.kind, dependency.source.repository))} : {}), + ? { directory: selected.get(identity(dependency.kind, dependency.source.repository)) } + : {}), })); } else { - const resource = graph.resources.find((entry: {directory: string}) => entry.directory === directory); + const resource = graph.resources.find((entry: { directory: string }) => entry.directory === directory); if (resource) resource.dependencies = dependencies; } } @@ -164,12 +229,22 @@ export async function convergeAuthoring(start: string, target = "root") { const temporary = `${graphFile}.${randomUUID()}.tmp`; try { await writeFile(temporary, graphAfter, { flag: "wx", mode: 0o600 }); - if (await readFile(graphFile, "utf8") !== graphBefore) throw new Error("Managed inventory changed during convergence; source is retained, retry after joining writers"); + if ((await readFile(graphFile, "utf8")) !== graphBefore) + throw new Error( + "Managed inventory changed during convergence; source is retained, retry after joining writers", + ); await rename(temporary, graphFile); - } finally { await rm(temporary, { force: true }); } + } finally { + await rm(temporary, { force: true }); + } } - return { workbench: context.workbench, converged: blockers.length === 0, - candidate: blockers.length ? null : complete.get(target) ?? null, + return { + workbench: context.workbench, + converged: blockers.length === 0, + candidate: blockers.length ? null : (complete.get(target) ?? null), retained: [...complete].map(([directory, source]) => ({ directory, source })), - worklist: blockers, verificationEvidence: false, activated: false }; + worklist: blockers, + verificationEvidence: false, + activated: false, + }; } diff --git a/src/capability-language/authoring-inspect.ts b/src/capability-language/authoring-inspect.ts index f0b0c9b..980bfc8 100644 --- a/src/capability-language/authoring-inspect.ts +++ b/src/capability-language/authoring-inspect.ts @@ -5,48 +5,74 @@ import path from "node:path"; import { parseQx, walkSyntax } from "./source.js"; import { authoringContext } from "./authoring-context.js"; import { readQxSource } from "./source-loader.js"; -import {loadQuixosLock} from "../resource-lock/index.js"; +import { loadQuixosLock } from "../resource-lock/index.js"; const execFile = promisify(callback); -const git = async (root: string, args: string[]) => (await execFile("git", ["-C", root, ...args], { - maxBuffer: 8 * 1024 * 1024, env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }, -})).stdout; +const git = async (root: string, args: string[]) => + ( + await execFile("git", ["-C", root, ...args], { + maxBuffer: 8 * 1024 * 1024, + env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }, + }) + ).stdout; const message = (error: unknown) => String(error instanceof Error ? error.message : error).slice(0, 2000); /** Syntax-only contract inspection is deliberately NOT verification evidence. * Each file can recover independently; current valid files always win. */ export async function inspectAuthoringRepository(root: string, historyLimit = 100) { const names = (await git(root, ["ls-files", "-z", "--cached", "--others", "--exclude-standard"])) - .split("\0").filter(name => name.endsWith(".qx")); - if (names.length > 128) throw new Error("Repository inspection exceeds 128 QX files; split the resource into smaller repositories"); + .split("\0") + .filter((name) => name.endsWith(".qx")); + if (names.length > 128) + throw new Error("Repository inspection exceeds 128 QX files; split the resource into smaller repositories"); const files = []; for (const name of [...new Set(names)].sort()) { - let source = "", errors: unknown[] = [], revision: string | null = null; + let source = "", + errors: unknown[] = [], + revision: string | null = null; try { source = await readQxSource(root, name); if (source.length > 262144) throw new Error(`Inspection file exceeds 256 KiB: ${name}`); errors = parseQx(source, name).diagnostics; - } catch (error) { errors = [{ message: message(error) }]; } + } catch (error) { + errors = [{ message: message(error) }]; + } const currentErrors = errors; if (errors.length) { // Git can traverse jj's immutable commit DAG without mutating/snapshotting @. - const head = await execFile("jj", ["--ignore-working-copy", "log", "--no-graph", "-r", "@", "-T", "commit_id"], - { cwd: root, env: { ...process.env, QUIXOS_JJ_NO_CHECKPOINT: "1" } }).then(r => r.stdout.trim(), () => "HEAD"); + const head = await execFile("jj", ["--ignore-working-copy", "log", "--no-graph", "-r", "@", "-T", "commit_id"], { + cwd: root, + env: { ...process.env, QUIXOS_JJ_NO_CHECKPOINT: "1" }, + }).then( + (r) => r.stdout.trim(), + () => "HEAD", + ); const commits = await git(root, ["rev-list", `--max-count=${historyLimit}`, head, "--", name]).catch(() => ""); for (const commit of commits.trim().split("\n").filter(Boolean)) { const historical = await git(root, ["show", `${commit}:${name}`]).catch(() => null); if (historical === null || historical.length > 262144) continue; if (!parseQx(historical, name).diagnostics.length) { - source = historical; revision = commit; errors = []; break; + source = historical; + revision = commit; + errors = []; + break; } } } const syntax = errors.length ? null : parseQx(source, name); - files.push({ file: name, status: errors.length ? "unavailable" : revision ? "historical" : "current", - revision, currentErrors: currentErrors.slice(0, 20), omittedErrors: Math.max(0, currentErrors.length - 20), - declarations: syntax ? [...walkSyntax(syntax.root)] - .filter(node => /^(?:interface|package|atom|state|edge|method|function|event|conformance)\w*Decl$/.test(node.kind)) - .map(node => ({ kind: node.kind, source: source.slice(node.start, node.end) })) : [], + files.push({ + file: name, + status: errors.length ? "unavailable" : revision ? "historical" : "current", + revision, + currentErrors: currentErrors.slice(0, 20), + omittedErrors: Math.max(0, currentErrors.length - 20), + declarations: syntax + ? [...walkSyntax(syntax.root)] + .filter((node) => + /^(?:interface|package|atom|state|edge|method|function|event|conformance)\w*Decl$/.test(node.kind), + ) + .map((node) => ({ kind: node.kind, source: source.slice(node.start, node.end) })) + : [], }); } return { verificationEvidence: false as const, resolutionChecked: false as const, files }; @@ -56,21 +82,32 @@ export async function inspectWorkbench(start: string, selector?: string) { const context = await authoringContext(start); if (!selector || selector === ".") { const relative = path.relative(context.workbench, await realpath(start)); - selector = context.resources.find(entry => relative === entry.directory || relative.startsWith(entry.directory + path.sep))?.directory ?? "root"; + selector = + context.resources.find((entry) => relative === entry.directory || relative.startsWith(entry.directory + path.sep)) + ?.directory ?? "root"; } const lock = await loadQuixosLock(path.join(context.workbench, "root/quixos.lock")); - const aliases = lock.ok ? lock.lock.resources.filter(entry => entry.binding === selector) : []; - const selected = context.resources.filter(entry => !selector || selector === entry.directory || - selector === entry.resourceId || selector === path.basename(entry.directory) || aliases.some(alias => alias.kind === entry.kind && alias.source.repository === entry.source?.repository)); + const aliases = lock.ok ? lock.lock.resources.filter((entry) => entry.binding === selector) : []; + const selected = context.resources.filter( + (entry) => + !selector || + selector === entry.directory || + selector === entry.resourceId || + selector === path.basename(entry.directory) || + aliases.some((alias) => alias.kind === entry.kind && alias.source.repository === entry.source?.repository), + ); if (!selected.length) throw new Error(`No registered resource matches ${selector}`); - if (selector && selected.length > 1) throw new Error(`Ambiguous resource ${selector}; use its resource ID or directory`); + if (selector && selected.length > 1) + throw new Error(`Ambiguous resource ${selector}; use its resource ID or directory`); const resources = []; for (const entry of selected) { try { const root = path.join(context.workbench, entry.directory); - if (await realpath(root) !== root) throw new Error("Managed checkout crosses a symlink"); - resources.push({ ...entry, ...await inspectAuthoringRepository(root) }); - } catch (error) { resources.push({ ...entry, error: message(error) }); } + if ((await realpath(root)) !== root) throw new Error("Managed checkout crosses a symlink"); + resources.push({ ...entry, ...(await inspectAuthoringRepository(root)) }); + } catch (error) { + resources.push({ ...entry, error: message(error) }); + } } return { workbench: context.workbench, verificationEvidence: false, resources }; } diff --git a/src/capability-language/authoring-worklist.ts b/src/capability-language/authoring-worklist.ts index 3805562..148087e 100644 --- a/src/capability-language/authoring-worklist.ts +++ b/src/capability-language/authoring-worklist.ts @@ -11,50 +11,109 @@ import { checkerIdentity } from "./checked-build.js"; const execFile = promisify(callback); export async function authoringWorklist(start: string) { const context = await authoringContext(start); - const entries: {directory: string; resourceId?: string; phase: string; message: string; next: string}[] = []; + const entries: { directory: string; resourceId?: string; phase: string; message: string; next: string }[] = []; const dependencies = new Map(); for (const resource of context.resources) { const root = path.join(context.workbench, resource.directory); - const add = (phase: string, message: string) => entries.push({directory: resource.directory, resourceId: resource.resourceId, phase, message, - next: phase === "syntax" ? `qx-workspace inspect ${resource.directory}` : phase === "evolution" ? "Inspect evolution.json in the check output; resolve its named migration/review requirements before cutover" : `cd ${resource.directory} && qx-workspace check`}); + const add = (phase: string, message: string) => + entries.push({ + directory: resource.directory, + resourceId: resource.resourceId, + phase, + message, + next: + phase === "syntax" + ? `qx-workspace inspect ${resource.directory}` + : phase === "evolution" + ? "Inspect evolution.json in the check output; resolve its named migration/review requirements before cutover" + : `cd ${resource.directory} && qx-workspace check`, + }); try { - if (await fs.realpath(root) !== root) throw new Error("Registered checkout crosses a symlink"); + if ((await fs.realpath(root)) !== root) throw new Error("Registered checkout crosses a symlink"); const inspected = await inspectAuthoringRepository(root); - for (const file of inspected.files) if (file.currentErrors.length) add("syntax", `${file.file}: ${JSON.stringify(file.currentErrors)}${file.status === "historical" ? `; historical contract available at ${file.revision}` : ""}`); + for (const file of inspected.files) + if (file.currentErrors.length) + add( + "syntax", + `${file.file}: ${JSON.stringify(file.currentErrors)}${file.status === "historical" ? `; historical contract available at ${file.revision}` : ""}`, + ); const lock = await loadQuixosLock(path.join(root, "quixos.lock")); - if (!lock.ok) add("resolution", lock.diagnostics.map(entry => `${entry.fileName}: ${entry.message}`).join("\n")); - else dependencies.set(resource.directory, lock.lock.resources.flatMap(dependency => { - const selected = context.resources.find(entry => entry.kind === dependency.kind && entry.source?.repository === dependency.source.repository); - if (selected?.source && selected.source.commit !== dependency.source.commit) add("propagation", `Dependency ${dependency.binding} has advanced; check will repin it automatically`); - return selected ? [selected.directory] : []; - })); + if (!lock.ok) + add("resolution", lock.diagnostics.map((entry) => `${entry.fileName}: ${entry.message}`).join("\n")); + else + dependencies.set( + resource.directory, + lock.lock.resources.flatMap((dependency) => { + const selected = context.resources.find( + (entry) => entry.kind === dependency.kind && entry.source?.repository === dependency.source.repository, + ); + if (selected?.source && selected.source.commit !== dependency.source.commit) + add("propagation", `Dependency ${dependency.binding} has advanced; check will repin it automatically`); + return selected ? [selected.directory] : []; + }), + ); let record; - try { record = JSON.parse(await fs.readFile(path.join(context.workbench, ".quixos/checks", checkRecordName(resource.directory)), "utf8")); } - catch (error) { if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; } + try { + record = JSON.parse( + await fs.readFile( + path.join(context.workbench, ".quixos/checks", checkRecordName(resource.directory)), + "utf8", + ), + ); + } catch (error) { + if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; + } if (!record?.commit) { if (record?.blockers?.length) add(record.phase, record.blockers.join("\n")); else add("unchecked", "No immutable candidate check recorded yet"); continue; } if (record.checker !== checkerIdentity()) add("unchecked", "The installed checker changed since the last check"); - const env = {...process.env, QUIXOS_JJ_NO_CHECKPOINT: "1"}; - const commit = (await execFile("jj", ["--ignore-working-copy", "log", "--no-graph", "-r", "@", "-T", "commit_id"], {cwd: root, env})).stdout.trim(); - const dirty = await execFile("git", ["diff", "--quiet", "--no-ext-diff", record.commit, "--"], {cwd: root}).then(() => false, () => true); - const untracked = (await execFile("git", ["ls-files", "--others", "--exclude-standard"], {cwd: root})).stdout; - if (commit !== record.commit || dirty || untracked) add("unchecked", `Edits are newer than the last check (${record.commit.slice(0, 12)})`); + const env = { ...process.env, QUIXOS_JJ_NO_CHECKPOINT: "1" }; + const commit = ( + await execFile("jj", ["--ignore-working-copy", "log", "--no-graph", "-r", "@", "-T", "commit_id"], { + cwd: root, + env, + }) + ).stdout.trim(); + const dirty = await execFile("git", ["diff", "--quiet", "--no-ext-diff", record.commit, "--"], { + cwd: root, + }).then( + () => false, + () => true, + ); + const untracked = (await execFile("git", ["ls-files", "--others", "--exclude-standard"], { cwd: root })).stdout; + if (commit !== record.commit || dirty || untracked) + add("unchecked", `Edits are newer than the last check (${record.commit.slice(0, 12)})`); else if (record.blockers?.length) add(record.phase, record.blockers.join("\n")); - } catch (error) { add("inspection", String(error).slice(0, 3000)); } + } catch (error) { + add("inspection", String(error).slice(0, 3000)); + } } // Fixed-point propagation, independent of registration order. - const blocked = new Set(entries.map(entry => entry.directory)); + const blocked = new Set(entries.map((entry) => entry.directory)); let changed = true; while (changed) { changed = false; - for (const [directory, required] of dependencies) if (!blocked.has(directory)) { - const waiting = required.filter(dependency => blocked.has(dependency)); - if (waiting.length) { blocked.add(directory); changed = true; entries.push({directory, phase: "dependency", message: `Waiting for ${waiting.join(", ")}`, next: "Resolve the named repositories, then rerun check"}); } - } + for (const [directory, required] of dependencies) + if (!blocked.has(directory)) { + const waiting = required.filter((dependency) => blocked.has(dependency)); + if (waiting.length) { + blocked.add(directory); + changed = true; + entries.push({ + directory, + phase: "dependency", + message: `Waiting for ${waiting.join(", ")}`, + next: "Resolve the named repositories, then rerun check", + }); + } + } } - return { workbench: context.workbench, verificationEvidence: false, worklist: entries, - note: "Derived authoring guidance, not activation approval. Independent repositories can be delegated separately; join writers before a root check." }; + return { + workbench: context.workbench, + verificationEvidence: false, + worklist: entries, + note: "Derived authoring guidance, not activation approval. Independent repositories can be delegated separately; join writers before a root check.", + }; } diff --git a/src/capability-language/candidate-check.ts b/src/capability-language/candidate-check.ts index 8c77de8..6ccb5ed 100644 --- a/src/capability-language/candidate-check.ts +++ b/src/capability-language/candidate-check.ts @@ -6,34 +6,51 @@ import { promisify } from "node:util"; import { createHash } from "node:crypto"; import { compileWorkspaceRepository, compileCapabilityResourceRepository } from "./assembly.js"; import { createGitCapabilityResolver } from "./git-resolver.js"; -import { contentDigest, planEvolution, type EvolutionReview, type WorkspaceRevision } from "../capability-model/index.js"; +import { + contentDigest, + planEvolution, + type EvolutionReview, + type WorkspaceRevision, +} from "../capability-model/index.js"; import { bindingSchema } from "../bindings/index.js"; -import {snapshotCommit, checkoutCommit, buildCheckedPackage} from "./checked-build.js"; +import { snapshotCommit, checkoutCommit, buildCheckedPackage } from "./checked-build.js"; const execFile = promisify(execFileCallback); const bytesDigest = (value: Uint8Array) => `sha256:${createHash("sha256").update(value).digest("hex")}`; -export const localResourceSnapshots = async (root: string, filename?: string): Promise<{resources: {kind: string; repository: string; commit: string; directory: string}[]}> => { +export const localResourceSnapshots = async ( + root: string, + filename?: string, +): Promise<{ resources: { kind: string; repository: string; commit: string; directory: string }[] }> => { if (filename) { const document = JSON.parse(await fs.readFile(filename, "utf8")); - return {resources: document.resources.map((entry: {directory: string}) => ({...entry, directory: path.resolve(path.dirname(filename), entry.directory)}))}; + return { + resources: document.resources.map((entry: { directory: string }) => ({ + ...entry, + directory: path.resolve(path.dirname(filename), entry.directory), + })), + }; } let directory = await fs.realpath(root); for (;;) { let graphText: string | undefined; - try {graphText = await fs.readFile(path.join(directory, ".quixos/resource-graph.json"), "utf8");} - catch (error) {if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;} + try { + graphText = await fs.readFile(path.join(directory, ".quixos/resource-graph.json"), "utf8"); + } catch (error) { + if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; + } if (graphText !== undefined) { const graph = JSON.parse(graphText); const resources = []; for (const entry of graph.resources) { const location = await fs.realpath(path.resolve(directory, entry.directory)); - if (!location.startsWith(`${directory}/resources/`)) throw new Error("Workbench resource escapes managed directory"); - resources.push({kind: entry.kind, ...entry.source, directory: location}); + if (!location.startsWith(`${directory}/resources/`)) + throw new Error("Workbench resource escapes managed directory"); + resources.push({ kind: entry.kind, ...entry.source, directory: location }); } - return {resources}; + return { resources }; } const parent = path.dirname(directory); - if (parent === directory) return {resources: []}; + if (parent === directory) return { resources: [] }; directory = parent; } }; @@ -41,18 +58,33 @@ export const localResourceSnapshots = async (root: string, filename?: string): P /** Copy actual authoring files without snapshotting jj or creating a Git commit. */ export const snapshotRepository = async (source: string, destination: string) => { const root = await fs.realpath(source); - const files = async () => (await execFile("git", ["-C", root, "ls-files", "--cached", "--others", "--exclude-standard", "-z"], { maxBuffer: 16 * 1024 * 1024 })).stdout.split("\0").filter(Boolean).sort(); + const files = async () => + ( + await execFile("git", ["-C", root, "ls-files", "--cached", "--others", "--exclude-standard", "-z"], { + maxBuffer: 16 * 1024 * 1024, + }) + ).stdout + .split("\0") + .filter(Boolean) + .sort(); const names = [...new Set(await files())]; if (names.length > 50_000) throw new Error("Candidate source exceeds 50000 files"); - const contents: {name: string; digest: string; mode: number}[] = []; + const contents: { name: string; digest: string; mode: number }[] = []; let bytes = 0; await fs.mkdir(destination, { recursive: true, mode: 0o700 }); for (const name of names) { - if (path.isAbsolute(name) || name.split(/[\\/]/).some((part) => part === ".." || part === ".git" || part === ".jj")) throw new Error("Invalid candidate source path"); + if (path.isAbsolute(name) || name.split(/[\\/]/).some((part) => part === ".." || part === ".git" || part === ".jj")) + throw new Error("Invalid candidate source path"); const file = path.join(root, name); let metadata; - try { metadata = await fs.lstat(file); } catch (error) { if ((error as NodeJS.ErrnoException).code === "ENOENT") continue; throw error; } - if (!metadata.isFile() || metadata.isSymbolicLink() || !(await fs.realpath(file)).startsWith(`${root}${path.sep}`)) throw new Error(`Candidate source must be a regular file: ${name}`); + try { + metadata = await fs.lstat(file); + } catch (error) { + if ((error as NodeJS.ErrnoException).code === "ENOENT") continue; + throw error; + } + if (!metadata.isFile() || metadata.isSymbolicLink() || !(await fs.realpath(file)).startsWith(`${root}${path.sep}`)) + throw new Error(`Candidate source must be a regular file: ${name}`); const data = await fs.readFile(file); bytes += data.length; if (bytes > 128 * 1024 * 1024) throw new Error("Candidate source exceeds 128 MiB"); @@ -60,28 +92,38 @@ export const snapshotRepository = async (source: string, destination: string) => await fs.mkdir(path.dirname(path.join(destination, name)), { recursive: true }); await fs.writeFile(path.join(destination, name), data, { flag: "wx", mode: metadata.mode & 0o777 }); } - if (JSON.stringify([...new Set(await files())]) !== JSON.stringify(names)) throw new Error("Source files changed during candidate snapshot"); - for (const entry of contents) if (bytesDigest(await fs.readFile(path.join(root, entry.name))) !== entry.digest) throw new Error(`Source changed during candidate snapshot: ${entry.name}`); + if (JSON.stringify([...new Set(await files())]) !== JSON.stringify(names)) + throw new Error("Source files changed during candidate snapshot"); + for (const entry of contents) + if (bytesDigest(await fs.readFile(path.join(root, entry.name))) !== entry.digest) + throw new Error(`Source changed during candidate snapshot: ${entry.name}`); return { source: root, directory: destination, treeDigest: contentDigest(contents), files: contents }; }; // Local mirrors accelerate resolution, but only their committed locked trees // may stand in for published dependencies. Never relabel dirty files as a pin. async function committedResolver(root: string, temporary: string, filename?: string, publishedOnly = false) { - const map = publishedOnly ? {resources: []} : await localResourceSnapshots(root, filename); + const map = publishedOnly ? { resources: [] } : await localResourceSnapshots(root, filename); const resources = []; for (const [index, entry] of map.resources.entries()) { const directory = path.join(temporary, `dependency-${index}`); await checkoutCommit(entry.directory, entry.commit, directory); - resources.push({...entry, directory}); + resources.push({ ...entry, directory }); } const snapshotMap = path.join(temporary, "snapshots.json"); - await fs.writeFile(snapshotMap, JSON.stringify({resources})); - return createGitCapabilityResolver({checkoutRoot: path.join(temporary, "resolved"), snapshotMap}); + await fs.writeFile(snapshotMap, JSON.stringify({ resources })); + return createGitCapabilityResolver({ checkoutRoot: path.join(temporary, "resolved"), snapshotMap }); } -export const checkResourceCandidate = async (options: {root: string; output: string; kind: "package" | "interface"; source: {repository: string; commit: string}; snapshotMap?: string; publishedOnly?: boolean}) => { - await fs.mkdir(options.output, {mode: 0o700}); +export const checkResourceCandidate = async (options: { + root: string; + output: string; + kind: "package" | "interface"; + source: { repository: string; commit: string }; + snapshotMap?: string; + publishedOnly?: boolean; +}) => { + await fs.mkdir(options.output, { mode: 0o700 }); const temporary = await fs.mkdtemp(path.join(os.tmpdir(), "qx-resource-check-")); const blockers: string[] = []; let treeDigest: string | undefined, commit: string | undefined, artifactPath: string | undefined; @@ -90,62 +132,128 @@ export const checkResourceCandidate = async (options: {root: string; output: str treeDigest = (await snapshotRepository(options.root, path.join(temporary, "observed"))).treeDigest; const root = path.join(temporary, "source"); await checkoutCommit(options.root, commit, root); - const resolveResource = await committedResolver(options.root, temporary, options.snapshotMap, options.publishedOnly); - const compiled = await compileCapabilityResourceRepository({rootDirectory: root, kind: options.kind, source: {resolver: "git", repository: options.source.repository, commit}, resolveResource}); + const resolveResource = await committedResolver( + options.root, + temporary, + options.snapshotMap, + options.publishedOnly, + ); + const compiled = await compileCapabilityResourceRepository({ + rootDirectory: root, + kind: options.kind, + source: { resolver: "git", repository: options.source.repository, commit }, + resolveResource, + }); if (compiled.resource.kind === "package") { const schema = path.join(temporary, "bindings.json"); await fs.writeFile(schema, JSON.stringify(bindingSchema(compiled))); artifactPath = await buildCheckedPackage(root, schema, compiled.resource.revision.revisionId); } - if (await snapshotCommit(options.root) !== commit) throw new Error("Source changed during verification; run the check again"); + if ((await snapshotCommit(options.root)) !== commit) + throw new Error("Source changed during verification; run the check again"); await fs.writeFile(path.join(options.output, "candidate.json"), JSON.stringify(compiled.resource, null, 2)); } catch (error) { blockers.push(error instanceof Error ? error.message : String(error)); - } finally {await fs.rm(temporary, {recursive: true, force: true});} - const result = {candidateOnly: true, activationEvidence: false, commit, treeDigest, artifactPath, blockers, - note: "Checked immutable candidate; cutover independently checks current migration/review requirements. No publication or activation performed."}; + } finally { + await fs.rm(temporary, { recursive: true, force: true }); + } + const result = { + candidateOnly: true, + activationEvidence: false, + commit, + treeDigest, + artifactPath, + blockers, + note: "Checked immutable candidate; cutover independently checks current migration/review requirements. No publication or activation performed.", + }; await fs.writeFile(path.join(options.output, "report.json"), JSON.stringify(result, null, 2)); return result; }; -export const checkWorkspaceCandidate = async (options: {root: string; output: string; snapshotMap?: string; baseline?: string; reviews?: string}) => { - await fs.mkdir(options.output, {mode: 0o700}); +export const checkWorkspaceCandidate = async (options: { + root: string; + output: string; + snapshotMap?: string; + baseline?: string; + reviews?: string; +}) => { + await fs.mkdir(options.output, { mode: 0o700 }); const temporary = await fs.mkdtemp(path.join(os.tmpdir(), "qx-workspace-check-")); - const blockers: string[] = [], checks: {packageRevisionId: string; artifactPath: string}[] = []; + const blockers: string[] = [], + checks: { packageRevisionId: string; artifactPath: string }[] = []; let commit: string | undefined; try { commit = await snapshotCommit(options.root); for (const entry of (await localResourceSnapshots(options.root, options.snapshotMap)).resources) { const current = await snapshotCommit(entry.directory); - const tree = async (revision: string) => (await execFile("git", ["rev-parse", `${revision}^{tree}`], {cwd: entry.directory})).stdout.trim(); - if (await tree(current) !== await tree(entry.commit)) throw new Error(`Edited resource is not in the root's locked candidate: ${entry.directory}. Check that resource, then run qx-workspace resource upgrade --publish to propagate its revision.`); + const tree = async (revision: string) => + (await execFile("git", ["rev-parse", `${revision}^{tree}`], { cwd: entry.directory })).stdout.trim(); + if ((await tree(current)) !== (await tree(entry.commit))) + throw new Error( + `Edited resource is not in the root's locked candidate: ${entry.directory}. Check that resource, then run qx-workspace resource upgrade --publish to propagate its revision.`, + ); } const root = path.join(temporary, "source"); await checkoutCommit(options.root, commit, root); const resolveResource = await committedResolver(options.root, temporary, options.snapshotMap); - const compiled = await compileWorkspaceRepository({rootDirectory: root, sourceRootCommit: commit, resolveResource}); - const baseline = options.baseline ? JSON.parse(await fs.readFile(options.baseline, "utf8")) as WorkspaceRevision : null; - const reviews = options.reviews ? JSON.parse(await fs.readFile(options.reviews, "utf8")) as EvolutionReview[] : []; - const evolution = planEvolution(baseline, compiled.workspace, {reviews}); + const compiled = await compileWorkspaceRepository({ + rootDirectory: root, + sourceRootCommit: commit, + resolveResource, + }); + const baseline = options.baseline + ? (JSON.parse(await fs.readFile(options.baseline, "utf8")) as WorkspaceRevision) + : null; + const reviews = options.reviews + ? (JSON.parse(await fs.readFile(options.reviews, "utf8")) as EvolutionReview[]) + : []; + const evolution = planEvolution(baseline, compiled.workspace, { reviews }); blockers.push(...evolution.blockers); - for (const resource of compiled.resources.filter(entry => entry.kind === "package")) { + for (const resource of compiled.resources.filter((entry) => entry.kind === "package")) { // Per-package recursive schema, identical to host activation, not unrelated // workspace declarations that would unnecessarily invalidate build caches. - const candidate = await compileCapabilityResourceRepository({rootDirectory: resource.directory, kind: "package", source: resource.source, resolveResource}); + const candidate = await compileCapabilityResourceRepository({ + rootDirectory: resource.directory, + kind: "package", + source: resource.source, + resolveResource, + }); const schema = path.join(temporary, "bindings.json"); await fs.writeFile(schema, JSON.stringify(bindingSchema(candidate))); - const artifactPath = await buildCheckedPackage(resource.directory, schema, candidate.resource.revision.revisionId); - checks.push({packageRevisionId: candidate.resource.revision.revisionId, artifactPath}); + const artifactPath = await buildCheckedPackage( + resource.directory, + schema, + candidate.resource.revision.revisionId, + ); + checks.push({ packageRevisionId: candidate.resource.revision.revisionId, artifactPath }); } - if (await snapshotCommit(options.root) !== commit) throw new Error("Source changed during verification; run the check again"); - const result = {schemaVersion: 1, candidateOnly: true, activationEvidence: false, commit, evolution, checks, blockers, - note: "Checks the committed root and its exact locked dependencies. Resource edits must be verified and repinned before they enter this candidate. No publication or activation performed."}; + if ((await snapshotCommit(options.root)) !== commit) + throw new Error("Source changed during verification; run the check again"); + const result = { + schemaVersion: 1, + candidateOnly: true, + activationEvidence: false, + commit, + evolution, + checks, + blockers, + note: "Checks the committed root and its exact locked dependencies. Resource edits must be verified and repinned before they enter this candidate. No publication or activation performed.", + }; await fs.writeFile(path.join(options.output, "candidate.json"), JSON.stringify(compiled.workspace, null, 2)); await fs.writeFile(path.join(options.output, "report.json"), JSON.stringify(result, null, 2)); return result; } catch (error) { - const result = {schemaVersion: 1, candidateOnly: true, activationEvidence: false, commit, checks, blockers: [...blockers, error instanceof Error ? error.message : String(error)]}; + const result = { + schemaVersion: 1, + candidateOnly: true, + activationEvidence: false, + commit, + checks, + blockers: [...blockers, error instanceof Error ? error.message : String(error)], + }; await fs.writeFile(path.join(options.output, "report.json"), JSON.stringify(result, null, 2)); return result; - } finally {await fs.rm(temporary, {recursive: true, force: true});} + } finally { + await fs.rm(temporary, { recursive: true, force: true }); + } }; diff --git a/src/capability-language/checked-build.ts b/src/capability-language/checked-build.ts index 2a3f63e..d08b50a 100644 --- a/src/capability-language/checked-build.ts +++ b/src/capability-language/checked-build.ts @@ -1,38 +1,52 @@ import fs from "node:fs/promises"; import path from "node:path"; -import {execFile as callback, spawn} from "node:child_process"; +import { execFile as callback, spawn } from "node:child_process"; import { createWriteStream } from "node:fs"; -import {promisify} from "node:util"; -import {fileURLToPath} from "node:url"; +import { promisify } from "node:util"; +import { fileURLToPath } from "node:url"; const execFile = promisify(callback); -const environment = () => ({...process.env, QUIXOS_JJ_NO_CHECKPOINT: "1", GIT_TERMINAL_PROMPT: "0"}); -export const checkerIdentity = () => process.env.QUIXOS_CHECK_GENERATOR ?? path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); +const environment = () => ({ ...process.env, QUIXOS_JJ_NO_CHECKPOINT: "1", GIT_TERMINAL_PROMPT: "0" }); +export const checkerIdentity = () => + process.env.QUIXOS_CHECK_GENERATOR ?? path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); /** Checking snapshots jj, but never publishes or activates the working copy. */ export async function snapshotCommit(root: string): Promise { - const run = async (...args: string[]) => (await execFile("jj", args, {cwd: root, env: environment()})).stdout.trim(); + const run = async (...args: string[]) => + (await execFile("jj", args, { cwd: root, env: environment() })).stdout.trim(); await run("status"); // jj resolve --list exits 1 on a clean revision. Query structured revision // metadata instead of depending on diagnostic wording or swallowing errors. - if (await run("--ignore-working-copy", "log", "--no-graph", "-r", "@", "-T", "conflict") !== "false") throw new Error("Resolve source conflicts before verification"); + if ((await run("--ignore-working-copy", "log", "--no-graph", "-r", "@", "-T", "conflict")) !== "false") + throw new Error("Resolve source conflicts before verification"); const commit = await run("--ignore-working-copy", "log", "--no-graph", "-r", "@", "-T", "commit_id"); if (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(commit)) throw new Error("Verification requires an exact jj commit"); - await execFile("git", ["diff", "--exit-code", "--no-ext-diff", "--no-textconv", commit, "--"], {cwd: root, env: environment()}); - const {stdout} = await execFile("git", ["ls-files", "--others", "--exclude-standard", "-z"], {cwd: root}); + await execFile("git", ["diff", "--exit-code", "--no-ext-diff", "--no-textconv", commit, "--"], { + cwd: root, + env: environment(), + }); + const { stdout } = await execFile("git", ["ls-files", "--others", "--exclude-standard", "-z"], { cwd: root }); if (stdout) throw new Error("Source contains files not captured by jj; inspect jj tracking before verification"); return commit; } /** Use Git's actual committed tree, never dirty overlays labelled as old pins. */ export async function checkoutCommit(root: string, commit: string, destination: string) { - await fs.mkdir(destination, {recursive: true}); - const archive = await execFile("git", ["archive", "--format=tar", commit], {cwd: root, encoding: "buffer", maxBuffer: 128 * 1024 * 1024}); + await fs.mkdir(destination, { recursive: true }); + const archive = await execFile("git", ["archive", "--format=tar", commit], { + cwd: root, + encoding: "buffer", + maxBuffer: 128 * 1024 * 1024, + }); await new Promise((resolve, reject) => { - const child = spawn("tar", ["-xf", "-", "-C", destination], {stdio: ["pipe", "ignore", "pipe"]}); + const child = spawn("tar", ["-xf", "-", "-C", destination], { stdio: ["pipe", "ignore", "pipe"] }); let error = ""; - child.stderr.on("data", chunk => {error += chunk;}); + child.stderr.on("data", (chunk) => { + error += chunk; + }); child.on("error", reject); - child.on("close", code => code === 0 ? resolve() : reject(new Error(`Cannot extract committed source: ${error}`))); + child.on("close", (code) => + code === 0 ? resolve() : reject(new Error(`Cannot extract committed source: ${error}`)), + ); child.stdin.on("error", reject); child.stdin.end(archive.stdout); }); @@ -43,21 +57,47 @@ export async function checkoutCommit(root: string, commit: string, destination: * not a certificate authored or approved by the workspace agent. */ export async function buildCheckedPackage(source: string, schema: string, packageRevisionId: string): Promise { - const generator = process.env.QUIXOS_CHECK_GENERATOR ?? path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); - if (!/^\/nix\/store\/[^/]+$/.test(generator)) throw new Error("Run verification with the installed Quixos tooling (its exact Nix checker is required)"); + const generator = + process.env.QUIXOS_CHECK_GENERATOR ?? path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); + if (!/^\/nix\/store\/[^/]+$/.test(generator)) + throw new Error("Run verification with the installed Quixos tooling (its exact Nix checker is required)"); const builder = path.join(generator, "share/checked-package.nix"); await fs.access(builder); return await new Promise((resolve, reject) => { - const child = spawn("nix", ["build", "--impure", "--file", builder, - "--argstr", "source", source, "--argstr", "schema", schema, - "--argstr", "generator", generator, "--argstr", "packageRevisionId", packageRevisionId, - "--no-link", "--print-out-paths", "-L"], {env: environment(), stdio: ["ignore", "pipe", "inherit"]}); + const child = spawn( + "nix", + [ + "build", + "--impure", + "--file", + builder, + "--argstr", + "source", + source, + "--argstr", + "schema", + schema, + "--argstr", + "generator", + generator, + "--argstr", + "packageRevisionId", + packageRevisionId, + "--no-link", + "--print-out-paths", + "-L", + ], + { env: environment(), stdio: ["ignore", "pipe", "inherit"] }, + ); let output = ""; - child.stdout.on("data", chunk => {output += chunk;}); + child.stdout.on("data", (chunk) => { + output += chunk; + }); child.on("error", reject); - child.on("close", code => { + child.on("close", (code) => { const artifact = output.trim(); - if (code !== 0 || !/^\/nix\/store\/[a-z0-9]{32}-[^\s/]+$/.test(artifact)) reject(new Error(`Checked Nix build failed (${code}); see build diagnostics above`)); + if (code !== 0 || !/^\/nix\/store\/[a-z0-9]{32}-[^\s/]+$/.test(artifact)) + reject(new Error(`Checked Nix build failed (${code}); see build diagnostics above`)); else resolve(artifact); }); }); @@ -65,31 +105,80 @@ export async function buildCheckedPackage(source: string, schema: string, packag /** Exact remote source DAG; the Nix checker owns schema construction and all * nested fetches. Keep build noise in a named log, with a bounded failure tail. */ -export async function buildImmutableCandidate(source: { repository: string; commit: string }, kind: "workspace" | "interface" | "package", logFile: string, contractOnly = false): Promise { - if (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(source.commit)) throw new Error("An immutable candidate requires an exact commit"); +export async function buildImmutableCandidate( + source: { repository: string; commit: string }, + kind: "workspace" | "interface" | "package", + logFile: string, + contractOnly = false, +): Promise { + if (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(source.commit)) + throw new Error("An immutable candidate requires an exact commit"); const url = new URL(source.repository); - if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash) throw new Error("Candidate origin must be credential-free HTTPS"); - const generator = process.env.QUIXOS_CHECK_GENERATOR ?? path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); + if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash) + throw new Error("Candidate origin must be credential-free HTTPS"); + const generator = + process.env.QUIXOS_CHECK_GENERATOR ?? path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); if (!/^\/nix\/store\/[^/]+$/.test(generator)) throw new Error("Use the installed Quixos checker"); const builder = path.join(generator, "share/checked-candidate.nix"); await fs.access(builder); const log = createWriteStream(logFile, { flags: "wx", mode: 0o600 }); - await new Promise((resolve, reject) => { log.once("open", () => resolve()); log.once("error", reject); }); + await new Promise((resolve, reject) => { + log.once("open", () => resolve()); + log.once("error", reject); + }); return await new Promise((resolve, reject) => { - let output = "", tail = "", failure: Error | undefined; - const child = spawn("nix", ["build", "--impure", "--file", builder, - "--argstr", "repository", source.repository, "--argstr", "commit", source.commit, - "--argstr", "kind", kind, "--argstr", "generator", generator, - "--arg", "contractOnly", contractOnly ? "true" : "false", - "--no-link", "--print-out-paths", "-L"], { env: environment(), stdio: ["ignore", "pipe", "pipe"] }); - log.on("error", error => { failure = error; child.kill(); }); - child.stdout.on("data", chunk => { output += chunk; }); - child.stderr.on("data", chunk => { log.write(chunk); tail = (tail + String(chunk)).slice(-6000); }); - child.on("error", error => { failure = error; }); - child.on("close", code => log.end(() => { - if (failure) reject(failure); - else if (code !== 0 || !/^\/nix\/store\/[a-z0-9]{32}-[^\s/]+$/.test(output.trim())) reject(new Error(`Candidate Nix check failed (${code}). Full log: ${logFile}\n${tail}`)); - else resolve(output.trim()); - })); + let output = "", + tail = "", + failure: Error | undefined; + const child = spawn( + "nix", + [ + "build", + "--impure", + "--file", + builder, + "--argstr", + "repository", + source.repository, + "--argstr", + "commit", + source.commit, + "--argstr", + "kind", + kind, + "--argstr", + "generator", + generator, + "--arg", + "contractOnly", + contractOnly ? "true" : "false", + "--no-link", + "--print-out-paths", + "-L", + ], + { env: environment(), stdio: ["ignore", "pipe", "pipe"] }, + ); + log.on("error", (error) => { + failure = error; + child.kill(); + }); + child.stdout.on("data", (chunk) => { + output += chunk; + }); + child.stderr.on("data", (chunk) => { + log.write(chunk); + tail = (tail + String(chunk)).slice(-6000); + }); + child.on("error", (error) => { + failure = error; + }); + child.on("close", (code) => + log.end(() => { + if (failure) reject(failure); + else if (code !== 0 || !/^\/nix\/store\/[a-z0-9]{32}-[^\s/]+$/.test(output.trim())) + reject(new Error(`Candidate Nix check failed (${code}). Full log: ${logFile}\n${tail}`)); + else resolve(output.trim()); + }), + ); }); } diff --git a/src/capability-language/cli.ts b/src/capability-language/cli.ts index 54bb9d7..bc879dc 100644 --- a/src/capability-language/cli.ts +++ b/src/capability-language/cli.ts @@ -35,9 +35,9 @@ const parseArgs = (args: string[]) => { else positional.push(argument); } if ( - positional.length !== 1 - || Boolean(workspaceId) !== Boolean(workspaceRevisionId) - || (resource && Boolean(workspaceId || workspaceRevisionId || sourceRootCommit)) + positional.length !== 1 || + Boolean(workspaceId) !== Boolean(workspaceRevisionId) || + (resource && Boolean(workspaceId || workspaceRevisionId || sourceRootCommit)) ) { throw new Error(usage); } @@ -50,14 +50,7 @@ const main = async () => { process.stdout.write(`${usage}\n`); return; } - const { - checkOnly, - resource, - workspaceId, - workspaceRevisionId, - sourceRootCommit, - fileName, - } = parseArgs(args); + const { checkOnly, resource, workspaceId, workspaceRevisionId, sourceRootCommit, fileName } = parseArgs(args); const source = fileName === "-" ? await new Promise((resolve, reject) => { @@ -67,33 +60,33 @@ const main = async () => { process.stdin.on("error", reject); }) : await readFile(fileName, "utf8"); - const reportDiagnostics = (diagnostics: readonly { - fileName: string; - line: number; - column: number; - phase: string; - code: string; - message: string; - path?: string; - }[]) => { + const reportDiagnostics = ( + diagnostics: readonly { + fileName: string; + line: number; + column: number; + phase: string; + code: string; + message: string; + path?: string; + }[], + ) => { for (const diagnostic of diagnostics) { const location = diagnostic.line ? `${diagnostic.fileName}:${diagnostic.line}:${diagnostic.column + 1}` : `${diagnostic.fileName}${diagnostic.path ? `:${diagnostic.path}` : ""}`; - process.stderr.write( - `${location}: ${diagnostic.phase} ${diagnostic.code}: ${diagnostic.message}\n`, - ); + process.stderr.write(`${location}: ${diagnostic.phase} ${diagnostic.code}: ${diagnostic.message}\n`); } process.exitCode = 1; }; if (resource) { const result = compileCapabilityResourceSource(source, { - source: { - repository: "https://compiler.invalid/resource.git", - commit: "0000000000000000000000000000000000000000", - }, - fileName, - }); + source: { + repository: "https://compiler.invalid/resource.git", + commit: "0000000000000000000000000000000000000000", + }, + fileName, + }); if (!result.ok) { reportDiagnostics(result.diagnostics); return; @@ -106,14 +99,15 @@ const main = async () => { reportDiagnostics(result.diagnostics); return; } - const workspace = workspaceId && workspaceRevisionId - ? { - ...result.workspace, - workspaceId: capabilityId.workspace(workspaceId), - id: capabilityId.workspaceRevision(workspaceRevisionId), - ...(sourceRootCommit ? { sourceRootCommit } : {}), - } - : { ...result.workspace, ...(sourceRootCommit ? { sourceRootCommit } : {}) }; + const workspace = + workspaceId && workspaceRevisionId + ? { + ...result.workspace, + workspaceId: capabilityId.workspace(workspaceId), + id: capabilityId.workspaceRevision(workspaceRevisionId), + ...(sourceRootCommit ? { sourceRootCommit } : {}), + } + : { ...result.workspace, ...(sourceRootCommit ? { sourceRootCommit } : {}) }; const instantiated = compileWorkspaceRevision(workspace); if (!instantiated.ok) { throw new Error(instantiated.issues.map((issue) => `${issue.path}: ${issue.message}`).join("\n")); diff --git a/src/capability-language/file-lock.ts b/src/capability-language/file-lock.ts index 2c41173..a1419ba 100644 --- a/src/capability-language/file-lock.ts +++ b/src/capability-language/file-lock.ts @@ -3,19 +3,52 @@ import { spawn } from "node:child_process"; /** Kernel-owned lock: a crashed coordinator cannot leave a stale ownership file. * The persistent file is just an inode; EOF releases the helper's lock. */ export async function withFileLock(filename: string, work: () => Promise): Promise { - const child = spawn("flock", ["--exclusive", "--timeout", "120", "--conflict-exit-code", "75", filename, - process.execPath, "-e", 'process.stdout.write("locked\\n"); process.stdin.resume();'], {stdio: ["pipe", "pipe", "pipe"]}); + const child = spawn( + "flock", + [ + "--exclusive", + "--timeout", + "120", + "--conflict-exit-code", + "75", + filename, + process.execPath, + "-e", + 'process.stdout.write("locked\\n"); process.stdin.resume();', + ], + { stdio: ["pipe", "pipe", "pipe"] }, + ); let diagnostics = ""; - child.stdin.on("error", () => { /* acquisition/exit handling reports helper failure */ }); - child.stderr.on("data", chunk => { diagnostics = (diagnostics + String(chunk)).slice(-2000); }); - const closed = new Promise((resolve) => { child.once("close", () => resolve()); }); + child.stdin.on("error", () => { + /* acquisition/exit handling reports helper failure */ + }); + child.stderr.on("data", (chunk) => { + diagnostics = (diagnostics + String(chunk)).slice(-2000); + }); + const closed = new Promise((resolve) => { + child.once("close", () => resolve()); + }); try { await new Promise((resolve, reject) => { let output = ""; child.once("error", reject); - child.once("exit", code => reject(new Error(code === 75 ? "Timed out after 120 seconds waiting for another authoring command; inspect that command before retrying" : `Cannot acquire authoring lock: ${diagnostics}`))); - child.stdout.on("data", chunk => { output += chunk; if (output.includes("locked\n")) resolve(); }); + child.once("exit", (code) => + reject( + new Error( + code === 75 + ? "Timed out after 120 seconds waiting for another authoring command; inspect that command before retrying" + : `Cannot acquire authoring lock: ${diagnostics}`, + ), + ), + ); + child.stdout.on("data", (chunk) => { + output += chunk; + if (output.includes("locked\n")) resolve(); + }); }); return await work(); - } finally { child.stdin.end(); await closed; } + } finally { + child.stdin.end(); + await closed; + } } diff --git a/src/capability-language/git-resolver.ts b/src/capability-language/git-resolver.ts index bbd2f27..c87dcfc 100644 --- a/src/capability-language/git-resolver.ts +++ b/src/capability-language/git-resolver.ts @@ -57,24 +57,34 @@ export const createGitCapabilityResolver = async (options: { const existing = checkouts.get(key); if (existing) return await existing; const pending = (async () => { - const directory = path.join( - checkoutRoot, - checkoutName(kind, source.repository, source.commit), - ); + const directory = path.join(checkoutRoot, checkoutName(kind, source.repository, source.commit)); const verify = async (checkout: string) => { const { stdout } = await execFile("git", ["-C", checkout, "rev-parse", "HEAD"]); if (stdout.trim().toLowerCase() !== source.commit.toLowerCase()) { - throw new Error(`Locked commit mismatch for ${source.repository}: wanted ${source.commit}, fetched ${stdout.trim()}`); + throw new Error( + `Locked commit mismatch for ${source.repository}: wanted ${source.commit}, fetched ${stdout.trim()}`, + ); } - const { stdout: changes } = await execFile("git", ["-C", checkout, "status", "--porcelain", "--untracked-files=all"]); + const { stdout: changes } = await execFile("git", [ + "-C", + checkout, + "status", + "--porcelain", + "--untracked-files=all", + ]); if (changes.trim()) throw new Error(`Dependency checkout was modified: ${checkout}`); }; // Only complete, checked clones become visible under the deterministic name. // Concurrent resolvers may fetch independently, but cannot observe a partial clone. - if (await stat(directory).then(() => true, (error: NodeJS.ErrnoException) => { - if (error.code === "ENOENT") return false; - throw error; - })) { + if ( + await stat(directory).then( + () => true, + (error: NodeJS.ErrnoException) => { + if (error.code === "ENOENT") return false; + throw error; + }, + ) + ) { await verify(directory); return { directory }; } @@ -82,20 +92,21 @@ export const createGitCapabilityResolver = async (options: { const checkout = path.join(staging, "checkout"); try { await execFile("git", [ - "-c", - "advice.detachedHead=false", - "clone", - "--depth", - "1", - "--single-branch", - "--branch", - `quixos-reachability/${source.commit.toLowerCase()}`, - source.repository, - checkout, - ]); + "-c", + "advice.detachedHead=false", + "clone", + "--depth", + "1", + "--single-branch", + "--branch", + `quixos-reachability/${source.commit.toLowerCase()}`, + source.repository, + checkout, + ]); await verify(checkout); - try { await rename(checkout, directory); } - catch (error) { + try { + await rename(checkout, directory); + } catch (error) { if (!["EEXIST", "ENOTEMPTY"].includes((error as NodeJS.ErrnoException).code ?? "")) throw error; await verify(directory); } @@ -105,7 +116,11 @@ export const createGitCapabilityResolver = async (options: { return { directory }; })(); checkouts.set(key, pending); - try { return await pending; } - catch (error) { checkouts.delete(key); throw error; } + try { + return await pending; + } catch (error) { + checkouts.delete(key); + throw error; + } }; }; diff --git a/src/capability-language/implementation-edit.ts b/src/capability-language/implementation-edit.ts index abc9962..85ffb9b 100644 --- a/src/capability-language/implementation-edit.ts +++ b/src/capability-language/implementation-edit.ts @@ -1,25 +1,50 @@ -import {parse} from "@babel/parser"; -type Node = {type: string; start: number; end: number; [key: string]: unknown}; -const node = (value: unknown): value is Node => !!value && typeof value === "object" && typeof (value as Node).type === "string"; +import { parse } from "@babel/parser"; +type Node = { type: string; start: number; end: number; [key: string]: unknown }; +const node = (value: unknown): value is Node => + !!value && typeof value === "object" && typeof (value as Node).type === "string"; /** One requested insertion into ordinary authored TypeScript, not regeneration. * Ambiguous/custom wiring is left alone with an actionable error. */ -export function addImplementation(text: string, factory: "createRuntime" | "serveMigration", key: string, importPath: string, migrationOnly = false): string { - const ast = parse(text, {sourceType: "module", plugins: ["typescript"]}); +export function addImplementation( + text: string, + factory: "createRuntime" | "serveMigration", + key: string, + importPath: string, + migrationOnly = false, +): string { + const ast = parse(text, { sourceType: "module", plugins: ["typescript"] }); const objects: Node[] = []; const names = new Set(); const visit = (value: unknown) => { - if (Array.isArray(value)) { value.forEach(visit); return; } + if (Array.isArray(value)) { + value.forEach(visit); + return; + } if (!node(value)) return; if (value.type === "Identifier") names.add(String(value.name)); - if (value.type === "CallExpression" && node(value.callee) && value.callee.type === "Identifier" && value.callee.name === factory && - Array.isArray(value.arguments) && value.arguments.length === 1 && node(value.arguments[0]) && value.arguments[0].type === "ObjectExpression") objects.push(value.arguments[0]); + if ( + value.type === "CallExpression" && + node(value.callee) && + value.callee.type === "Identifier" && + value.callee.name === factory && + Array.isArray(value.arguments) && + value.arguments.length === 1 && + node(value.arguments[0]) && + value.arguments[0].type === "ObjectExpression" + ) + objects.push(value.arguments[0]); Object.values(value).forEach(visit); }; visit(ast); - if (objects.length !== 1) throw new Error(`Cannot safely add implementation: expected one ${factory}({...}) literal. Wire the handler in your authored server code instead.`); + if (objects.length !== 1) + throw new Error( + `Cannot safely add implementation: expected one ${factory}({...}) literal. Wire the handler in your authored server code instead.`, + ); const object = objects[0]; - if ((object.properties as Node[]).some(p => node(p.key) && !p.computed && (p.key.name === key || p.key.value === key))) throw new Error(`Implementation already exists for ${key}`); + if ( + (object.properties as Node[]).some((p) => node(p.key) && !p.computed && (p.key.name === key || p.key.value === key)) + ) + throw new Error(`Implementation already exists for ${key}`); let alias = "qxImplementation"; for (let index = 1; names.has(alias); index++) alias = `qxImplementation${index}`; const value = migrationOnly ? 'async () => { throw new Error("Migration-only export"); }' : alias; diff --git a/src/capability-language/inspect-cli.ts b/src/capability-language/inspect-cli.ts index e63e7ea..6db292c 100644 --- a/src/capability-language/inspect-cli.ts +++ b/src/capability-language/inspect-cli.ts @@ -10,7 +10,12 @@ for await (const chunk of process.stdin) { } const document = JSON.parse(input); if (!Array.isArray(document) && document?.operation === "instantiate-workspace") { - if (typeof document.source !== "string" || document.source.length > 262144 || typeof document.workspaceId !== "string") throw new Error("Invalid template identity request"); + if ( + typeof document.source !== "string" || + document.source.length > 262144 || + typeof document.workspaceId !== "string" + ) + throw new Error("Invalid template identity request"); process.stdout.write(JSON.stringify({ source: instantiateWorkspaceIdentity(document.source, document.workspaceId) })); process.exit(0); } diff --git a/src/capability-language/migration-seal.ts b/src/capability-language/migration-seal.ts index 0fa196e..583f090 100644 --- a/src/capability-language/migration-seal.ts +++ b/src/capability-language/migration-seal.ts @@ -1,8 +1,8 @@ import fs from "node:fs/promises"; import path from "node:path"; -import {contentDigest} from "../capability-model/evolution.js"; -import {validateMigrationCatalog, type MigrationCatalog} from "../capability-model/migrations.js"; -import {applyStructure, planStructure} from "./structural-plan.js"; +import { contentDigest } from "../capability-model/evolution.js"; +import { validateMigrationCatalog, type MigrationCatalog } from "../capability-model/migrations.js"; +import { applyStructure, planStructure } from "./structural-plan.js"; /** Explicitly acknowledge edited migration code. This does not change retained * contracts or grant activation approval; the immutable checker verifies it. */ @@ -17,7 +17,11 @@ export async function sealMigrations(directory: string) { migration.implementation.digest = contentDigest(await fs.readFile(implementation, "utf8")); } validateMigrationCatalog(catalog); - return applyStructure(await planStructure(root, {kind: "package", validation: "syntax", files: [ - {file, expected: before, replace: JSON.stringify(catalog, null, 2) + "\n"}, - ]})); + return applyStructure( + await planStructure(root, { + kind: "package", + validation: "syntax", + files: [{ file, expected: before, replace: JSON.stringify(catalog, null, 2) + "\n" }], + }), + ); } diff --git a/src/capability-language/parser.ts b/src/capability-language/parser.ts index 883dd7c..d928e0b 100644 --- a/src/capability-language/parser.ts +++ b/src/capability-language/parser.ts @@ -39,7 +39,8 @@ import { type WorkspaceRevision, } from "../capability-model/index.js"; import { QuixosCapabilityLexer } from "./generated/QuixosCapabilityLexer.js"; -import { QuixosCapabilityParser, +import { + QuixosCapabilityParser, type AttachmentDeclContext, type ConformanceDeclContext, type DependencyBindingBlockContext, @@ -110,10 +111,7 @@ export type CapabilityResourceCompileResult = | { ok: true; resource: CapabilityResource; diagnostics: [] } | { ok: false; diagnostics: CapabilitySourceDiagnostic[] }; -export type CapabilitySourceDiagnosticPhase = - | "syntax" - | "lowering" - | "validation"; +export type CapabilitySourceDiagnosticPhase = "syntax" | "lowering" | "validation"; export interface CapabilitySourceDiagnostic { phase: CapabilitySourceDiagnosticPhase; @@ -206,12 +204,7 @@ const contextPosition = (context: ParserRuleContext) => ({ column: context.start?.column ?? 0, }); -const loweringIssue = ( - state: LoweringState, - context: ParserRuleContext, - code: string, - message: string, -) => { +const loweringIssue = (state: LoweringState, context: ParserRuleContext, code: string, message: string) => { state.diagnostics.push({ phase: "lowering", code, @@ -229,8 +222,7 @@ const text = (context: { getText(): string } | null): string => { }; const identifier = text; -const stringValue = (context: { getText(): string } | null) => - JSON.parse(text(context)) as string; +const stringValue = (context: { getText(): string } | null) => JSON.parse(text(context)) as string; const declareSymbol = ( state: LoweringState, @@ -241,12 +233,7 @@ const declareSymbol = ( kind: string, ) => { if (table.has(name)) { - loweringIssue( - state, - context, - "duplicate-symbol", - `Duplicate ${kind} authoring name ${name}`, - ); + loweringIssue(state, context, "duplicate-symbol", `Duplicate ${kind} authoring name ${name}`); return; } table.set(name, value); @@ -261,19 +248,12 @@ const requireSymbol = ( ): Value | undefined => { const value = table.get(name); if (value === undefined) { - loweringIssue( - state, - context, - "unknown-symbol", - `Unknown ${kind} ${name}`, - ); + loweringIssue(state, context, "unknown-symbol", `Unknown ${kind} ${name}`); } return value; }; -const lowerCardinality = ( - context: { getText(): string } | null, -): EdgeCardinality => text(context) as EdgeCardinality; +const lowerCardinality = (context: { getText(): string } | null): EdgeCardinality => text(context) as EdgeCardinality; const lowerConstraint = ( state: LoweringState, @@ -287,22 +267,11 @@ const lowerConstraint = ( const atomId = requireSymbol(state, state.atoms, name, context, "atom"); return atomId ? { kind: "atom", atomId } : undefined; } - const symbol = requireSymbol( - state, - state.interfaces, - name, - context, - "interface", - ); - return symbol - ? { kind: "interface", interfaceRevisionId: symbol.revisionId } - : undefined; + const symbol = requireSymbol(state, state.interfaces, name, context, "interface"); + return symbol ? { kind: "interface", interfaceRevisionId: symbol.revisionId } : undefined; }; -const lowerValueType = ( - state: LoweringState, - context: ValueTypeContext | null, -): ValueType => { +const lowerValueType = (state: LoweringState, context: ValueTypeContext | null): ValueType => { if (!context) { throw new Error("Missing value type after a successful parse"); } @@ -320,9 +289,12 @@ const lowerValueType = ( return valueType.message(stringValue(context.stringLiteral())); } if (context.RECORD()) { - const fields = context.recordField().map((field) => [identifier(field.identifier()), lowerValueType(state, field.valueType())] as const); - if (new Set(fields.map(([name]) => name)).size !== fields.length) loweringIssue(state, context, "invalid-type", "Duplicate record field"); - return {kind: "record", fields: Object.fromEntries(fields)}; + const fields = context + .recordField() + .map((field) => [identifier(field.identifier()), lowerValueType(state, field.valueType())] as const); + if (new Set(fields.map(([name]) => name)).size !== fields.length) + loweringIssue(state, context, "invalid-type", "Duplicate record field"); + return { kind: "record", fields: Object.fromEntries(fields) }; } if (context.ATOM_REF()) { const name = identifier(context.identifier()); @@ -331,16 +303,8 @@ const lowerValueType = ( } if (context.INTERFACE_REF()) { const name = identifier(context.identifier()); - const symbol = requireSymbol( - state, - state.interfaces, - name, - context, - "interface", - ); - return valueType.interfaceRef( - symbol?.revisionId ?? capabilityId.interfaceRevision(`unresolved:${name}`), - ); + const symbol = requireSymbol(state, state.interfaces, name, context, "interface"); + return valueType.interfaceRef(symbol?.revisionId ?? capabilityId.interfaceRevision(`unresolved:${name}`)); } if (context.OPTIONAL()) { return valueType.optional(lowerValueType(state, context.valueType())); @@ -352,11 +316,7 @@ const lowerValueType = ( return valueType.unit; }; -const valueOperation = ( - displayName: string, - id: InterfaceOperation["id"], - value: ValueType, -): InterfaceOperation => { +const valueOperation = (displayName: string, id: InterfaceOperation["id"], value: ValueType): InterfaceOperation => { switch (displayName) { case "get": return { @@ -403,9 +363,7 @@ const relationshipOperation = ( cardinality: EdgeCardinality, ): InterfaceOperation => { const targetType = - target.kind === "atom" - ? valueType.atomRef(target.atomId) - : valueType.interfaceRef(target.interfaceRevisionId); + target.kind === "atom" ? valueType.atomRef(target.atomId) : valueType.interfaceRef(target.interfaceRevisionId); const resolvedType = cardinalityValueType(target, cardinality); switch (displayName) { case "resolve": @@ -447,42 +405,23 @@ const relationshipOperation = ( } }; -const lowerValueMember = ( - state: LoweringState, - context: ValueMemberContext, -): InterfaceMember => { +const lowerValueMember = (state: LoweringState, context: ValueMemberContext): InterfaceMember => { const memberName = identifier(context.identifier()); const memberValueType = lowerValueType(state, context.valueType()); const operations: InterfaceOperation[] = []; for (const operation of context.valueMemberOperation()) { if (operation.GET()) { operations.push( - valueOperation( - "get", - capabilityId.operation(stringValue(operation.stringLiteral(0))), - memberValueType, - ), + valueOperation("get", capabilityId.operation(stringValue(operation.stringLiteral(0))), memberValueType), ); } else if (operation.SET()) { operations.push( - valueOperation( - "set", - capabilityId.operation(stringValue(operation.stringLiteral(0))), - memberValueType, - ), + valueOperation("set", capabilityId.operation(stringValue(operation.stringLiteral(0))), memberValueType), ); } else { operations.push( - valueOperation( - "watch-start", - capabilityId.operation(stringValue(operation.stringLiteral(0))), - memberValueType, - ), - valueOperation( - "watch-stop", - capabilityId.operation(stringValue(operation.stringLiteral(1))), - memberValueType, - ), + valueOperation("watch-start", capabilityId.operation(stringValue(operation.stringLiteral(0))), memberValueType), + valueOperation("watch-stop", capabilityId.operation(stringValue(operation.stringLiteral(1))), memberValueType), ); } } @@ -561,10 +500,7 @@ const lowerRelationshipMember = ( }; }; -const lowerOperationMember = ( - state: LoweringState, - context: OperationMemberContext, -): InterfaceMember => { +const lowerOperationMember = (state: LoweringState, context: OperationMemberContext): InterfaceMember => { const inputType = lowerValueType(state, context.valueType(0)); const outputType = lowerValueType(state, context.valueType(1)); return { @@ -573,13 +509,15 @@ const lowerOperationMember = ( displayName: identifier(context.identifier()), inputType, outputType, - operations: [{ - id: capabilityId.operation(stringValue(context.stringLiteral(1))), - displayName: "call", - inputType, - outputType, - mode: "call", - }], + operations: [ + { + id: capabilityId.operation(stringValue(context.stringLiteral(1))), + displayName: "call", + inputType, + outputType, + mode: "call", + }, + ], }; }; @@ -598,10 +536,7 @@ const lowerInterface = ( if (operation) { return [lowerOperationMember(state, operation)]; } - const relationship = lowerRelationshipMember( - state, - entry.relationshipMember()!, - ); + const relationship = lowerRelationshipMember(state, entry.relationshipMember()!); return relationship ? [relationship] : []; }); const symbol = state.interfaces.get(alias)!; @@ -612,9 +547,7 @@ const lowerInterface = ( member.displayName, { memberId: member.id, - operations: new Map( - member.operations.map((operation) => [operation.displayName, operation.id]), - ), + operations: new Map(member.operations.map((operation) => [operation.displayName, operation.id])), }, context, "interface member", @@ -629,10 +562,7 @@ const lowerInterface = ( }; }; -const lowerDependencyPort = ( - state: LoweringState, - context: DependencyPortContext, -): DependencyPort | undefined => { +const lowerDependencyPort = (state: LoweringState, context: DependencyPortContext): DependencyPort | undefined => { const name = identifier(context.identifier(0)); const id = capabilityId.dependencyPort(stringValue(context.stringLiteral())); if (context.STATE()) { @@ -640,16 +570,9 @@ const lowerDependencyPort = ( .primitiveList()! .primitive() .map((entry) => text(entry)); - const invalid = primitives.filter( - (entry) => !["read", "write", "watch-start", "watch-stop"].includes(entry), - ); + const invalid = primitives.filter((entry) => !["read", "write", "watch-start", "watch-stop"].includes(entry)); if (invalid.length > 0) { - loweringIssue( - state, - context, - "invalid-port-primitive", - `State port cannot request ${invalid.join(", ")}`, - ); + loweringIssue(state, context, "invalid-port-primitive", `State port cannot request ${invalid.join(", ")}`); } return { id, @@ -671,18 +594,10 @@ const lowerDependencyPort = ( .primitive() .map((entry) => text(entry)); const invalid = primitives.filter( - (entry) => - !["resolve", "connect", "disconnect", "watch-start", "watch-stop"].includes( - entry, - ), + (entry) => !["resolve", "connect", "disconnect", "watch-start", "watch-stop"].includes(entry), ); if (invalid.length > 0) { - loweringIssue( - state, - context, - "invalid-port-primitive", - `Edge port cannot request ${invalid.join(", ")}`, - ); + loweringIssue(state, context, "invalid-port-primitive", `Edge port cannot request ${invalid.join(", ")}`); } return { id, @@ -697,13 +612,7 @@ const lowerDependencyPort = ( } const targetName = identifier(context.identifier(1)); if (context.INTERFACE()) { - const target = requireSymbol( - state, - state.interfaces, - targetName, - context, - "interface", - ); + const target = requireSymbol(state, state.interfaces, targetName, context, "interface"); if (target && !target.contractAvailable) { loweringIssue( state, @@ -729,8 +638,11 @@ const lowerDependencyPort = ( ? { id, displayName: name, - requirement: { kind: "constructor", atomId, - ...(context.valueType() ? { inputType: lowerValueType(state, context.valueType()) } : {}) }, + requirement: { + kind: "constructor", + atomId, + ...(context.valueType() ? { inputType: lowerValueType(state, context.valueType()) } : {}), + }, } : undefined; }; @@ -743,19 +655,15 @@ const lowerDependencyPorts = ( if (!block) { return []; } - return block - .dependencyPort() - .flatMap((entry) => { - const port = lowerDependencyPort(state, entry); - return port ? [port] : []; - }); + return block.dependencyPort().flatMap((entry) => { + const port = lowerDependencyPort(state, entry); + return port ? [port] : []; + }); }; const lowerReceiver = ( state: LoweringState, - context: PackageOperationExportContext["receiverRequirement"] extends () => infer Result - ? Result - : never, + context: PackageOperationExportContext["receiverRequirement"] extends () => infer Result ? Result : never, ): PackageReceiverRequirement => { if (context.ANY()) { return { kind: "any-object" }; @@ -764,9 +672,7 @@ const lowerReceiver = ( const name = identifier(context.identifier()); return { kind: "exact-atom", - atomId: - requireSymbol(state, state.atoms, name, context, "atom") ?? - capabilityId.atom(`unresolved:${name}`), + atomId: requireSymbol(state, state.atoms, name, context, "atom") ?? capabilityId.atom(`unresolved:${name}`), }; } const list = context.identifierList(); @@ -792,14 +698,7 @@ const registerPackageExport = ( const packageSymbol = state.packages.get(packageAlias)!; const portSymbols = new Map(); for (const port of entry.dependencyPorts) { - declareSymbol( - state, - portSymbols, - port.displayName, - port.id, - context, - "dependency port", - ); + declareSymbol(state, portSymbols, port.displayName, port.id, context, "dependency port"); } declareSymbol( state, @@ -860,8 +759,7 @@ const lowerPackageConstructor = ( const alias = identifier(context.identifier(0)); const atomName = identifier(context.identifier(1)); const atomId = - requireSymbol(state, state.atoms, atomName, context, "atom") ?? - capabilityId.atom(`unresolved:${atomName}`); + requireSymbol(state, state.atoms, atomName, context, "atom") ?? capabilityId.atom(`unresolved:${atomName}`); const entry: PackageExport = { kind: "constructor", id: capabilityId.packageExport(stringValue(context.stringLiteral())), @@ -890,11 +788,7 @@ const lowerPackage = ( if (fn) { return lowerPackageFunction(state, alias, fn); } - return lowerPackageConstructor( - state, - alias, - exportContext.packageConstructorExport()!, - ); + return lowerPackageConstructor(state, alias, exportContext.packageConstructorExport()!); }); return { packageId: capabilityId.package(stringValue(context.stringLiteral(0))), @@ -906,14 +800,10 @@ const lowerPackage = ( }; }; -const lowerState = ( - state: LoweringState, - context: StateDeclContext, -): StateSlotDefinition => { +const lowerState = (state: LoweringState, context: StateDeclContext): StateSlotDefinition => { const atomName = identifier(context.identifier(1)); const atomId = - requireSymbol(state, state.atoms, atomName, context, "atom") ?? - capabilityId.atom(`unresolved:${atomName}`); + requireSymbol(state, state.atoms, atomName, context, "atom") ?? capabilityId.atom(`unresolved:${atomName}`); const policy = context.storagePolicy(); const defaultContext = context.jsonLiteral(); let defaultValue: unknown; @@ -936,38 +826,30 @@ const lowerState = ( }; }; -const lowerEdgeEndpoint = ( - state: LoweringState, - context: EdgeEndpointContext, -): EdgeEndpoint | undefined => { +const lowerEdgeEndpoint = (state: LoweringState, context: EdgeEndpointContext): EdgeEndpoint | undefined => { const constraint = lowerConstraint(state, context.targetConstraint()); return constraint ? { - projectionId: capabilityId.edgeProjection( - stringValue(context.stringLiteral(0)), - ), + projectionId: capabilityId.edgeProjection(stringValue(context.stringLiteral(0))), displayName: identifier(context.identifier()), constraint, cardinality: lowerCardinality(context.cardinality()), ordered: Boolean(context.ORDERED()), ...(context.ON_DELETE() ? { onDelete: stringValue(context.stringLiteral(1)) as EdgeEndpoint["onDelete"] } : {}), ...(context.RETAIN_OTHER() ? { retainOther: true } : {}), - ...(context.KEYED() ? {keyType: stringValue(context.stringLiteral(context.ON_DELETE() ? 2 : 1)) as EdgeEndpoint["keyType"]} : {}), - ...(context.PUBLIC_TRAVERSAL() ? {publicTraversal: true} : {}), + ...(context.KEYED() + ? { keyType: stringValue(context.stringLiteral(context.ON_DELETE() ? 2 : 1)) as EdgeEndpoint["keyType"] } + : {}), + ...(context.PUBLIC_TRAVERSAL() ? { publicTraversal: true } : {}), } : undefined; }; -const lowerEdge = ( - state: LoweringState, - context: EdgeDeclContext, -): EdgeDefinition | undefined => { - const endpoints = context - .edgeEndpoint() - .flatMap((entry) => { - const endpoint = lowerEdgeEndpoint(state, entry); - return endpoint ? [endpoint] : []; - }); +const lowerEdge = (state: LoweringState, context: EdgeDeclContext): EdgeDefinition | undefined => { + const endpoints = context.edgeEndpoint().flatMap((entry) => { + const endpoint = lowerEdgeEndpoint(state, entry); + return endpoint ? [endpoint] : []; + }); if (endpoints.length !== 2) { return undefined; } @@ -979,46 +861,21 @@ const lowerEdge = ( }; }; -const lowerAttachment = ( - state: LoweringState, - context: AttachmentDeclContext, -): PersistentAttachment | undefined => { +const lowerAttachment = (state: LoweringState, context: AttachmentDeclContext): PersistentAttachment | undefined => { const stateContext = context.stateDecl(); - return stateContext - ? lowerState(state, stateContext) - : lowerEdge(state, context.edgeDecl()!); + return stateContext ? lowerState(state, stateContext) : lowerEdge(state, context.edgeDecl()!); }; -const registerAttachment = ( - state: LoweringState, - context: AttachmentDeclContext, - attachment: PersistentAttachment, -) => { +const registerAttachment = (state: LoweringState, context: AttachmentDeclContext, attachment: PersistentAttachment) => { const stateContext = context.stateDecl(); - const name = identifier( - stateContext ? stateContext.identifier(0) : context.edgeDecl()!.identifier(), - ); + const name = identifier(stateContext ? stateContext.identifier(0) : context.edgeDecl()!.identifier()); const projections = new Map(); if (attachment.kind === "edge") { for (const endpoint of attachment.endpoints) { - declareSymbol( - state, - projections, - endpoint.displayName, - endpoint.projectionId, - context, - "edge projection", - ); + declareSymbol(state, projections, endpoint.displayName, endpoint.projectionId, context, "edge projection"); } } - declareSymbol( - state, - state.attachments, - name, - { attachment, projections }, - context, - "attachment", - ); + declareSymbol(state, state.attachments, name, { attachment, projections }, context, "attachment"); }; const findOperationId = ( @@ -1028,30 +885,12 @@ const findOperationId = ( operationName: string, context: ParserRuleContext, ) => { - const interfaceSymbol = requireSymbol( - state, - state.interfaces, - interfaceAlias, - context, - "interface", - ); + const interfaceSymbol = requireSymbol(state, state.interfaces, interfaceAlias, context, "interface"); const member = interfaceSymbol - ? requireSymbol( - state, - interfaceSymbol.members, - memberName, - context, - `member on ${interfaceAlias}`, - ) + ? requireSymbol(state, interfaceSymbol.members, memberName, context, `member on ${interfaceAlias}`) : undefined; return member - ? requireSymbol( - state, - member.operations, - operationName, - context, - `operation on ${interfaceAlias}.${memberName}`, - ) + ? requireSymbol(state, member.operations, operationName, context, `operation on ${interfaceAlias}.${memberName}`) : undefined; }; @@ -1065,27 +904,12 @@ const lowerBoundDependencies = ( } return context.dependencyBinding().flatMap((entry) => { const portName = identifier(entry.identifier(0)); - const portId = requireSymbol( - state, - exportSymbol.ports, - portName, - entry, - "dependency port", - ); + const portId = requireSymbol(state, exportSymbol.ports, portName, entry, "dependency port"); if (!portId) { return []; } - const traversal = ( - edgeName: string, - projectionName: string, - ): EdgeTraversal | undefined => { - const attachment = requireSymbol( - state, - state.attachments, - edgeName, - entry, - "attachment", - ); + const traversal = (edgeName: string, projectionName: string): EdgeTraversal | undefined => { + const attachment = requireSymbol(state, state.attachments, edgeName, entry, "attachment"); if (!attachment || attachment.attachment.kind !== "edge") { if (attachment) { loweringIssue(state, entry, "wrong-attachment-kind", `${edgeName} is state, not an edge`); @@ -1099,57 +923,33 @@ const lowerBoundDependencies = ( entry, `projection on ${edgeName}`, ); - return projectionId - ? { edgeTypeId: attachment.attachment.id, projectionId } - : undefined; + return projectionId ? { edgeTypeId: attachment.attachment.id, projectionId } : undefined; }; if (entry.STATE()) { const attachmentName = identifier(entry.identifier(1)); - const attachment = requireSymbol( - state, - state.attachments, - attachmentName, - entry, - "attachment", - ); + const attachment = requireSymbol(state, state.attachments, attachmentName, entry, "attachment"); if (!attachment || attachment.attachment.kind !== "state") { if (attachment) { - loweringIssue( - state, - entry, - "wrong-attachment-kind", - `${attachmentName} is an edge, not state`, - ); + loweringIssue(state, entry, "wrong-attachment-kind", `${attachmentName} is an edge, not state`); } return []; } - const via = entry.VIA() - ? traversal(identifier(entry.identifier(2)), identifier(entry.identifier(3))) - : undefined; + const via = entry.VIA() ? traversal(identifier(entry.identifier(2)), identifier(entry.identifier(3))) : undefined; if (entry.VIA() && !via) return []; - return [{ - portId, - binding: { kind: "state", slotId: attachment.attachment.id, ...(via ? { via } : {}) }, - }]; + return [ + { + portId, + binding: { kind: "state", slotId: attachment.attachment.id, ...(via ? { via } : {}) }, + }, + ]; } if (entry.EDGE(0) && !entry.INTERFACE()) { const attachmentName = identifier(entry.identifier(1)); const projectionName = identifier(entry.identifier(2)); - const attachment = requireSymbol( - state, - state.attachments, - attachmentName, - entry, - "attachment", - ); + const attachment = requireSymbol(state, state.attachments, attachmentName, entry, "attachment"); if (!attachment || attachment.attachment.kind !== "edge") { if (attachment) { - loweringIssue( - state, - entry, - "wrong-attachment-kind", - `${attachmentName} is state, not an edge`, - ); + loweringIssue(state, entry, "wrong-attachment-kind", `${attachmentName} is state, not an edge`); } return []; } @@ -1160,9 +960,7 @@ const lowerBoundDependencies = ( entry, `projection on ${attachmentName}`, ); - const via = entry.VIA() - ? traversal(identifier(entry.identifier(3)), identifier(entry.identifier(4))) - : undefined; + const via = entry.VIA() ? traversal(identifier(entry.identifier(3)), identifier(entry.identifier(4))) : undefined; if (entry.VIA() && !via) return []; return projectionId ? [ @@ -1180,16 +978,8 @@ const lowerBoundDependencies = ( } if (entry.INTERFACE()) { const interfaceName = identifier(entry.identifier(1)); - const interfaceSymbol = requireSymbol( - state, - state.interfaces, - interfaceName, - entry, - "interface", - ); - const via = entry.VIA() - ? traversal(identifier(entry.identifier(2)), identifier(entry.identifier(3))) - : undefined; + const interfaceSymbol = requireSymbol(state, state.interfaces, interfaceName, entry, "interface"); + const via = entry.VIA() ? traversal(identifier(entry.identifier(2)), identifier(entry.identifier(3))) : undefined; if (entry.VIA() && !via) return []; return interfaceSymbol ? [ @@ -1206,9 +996,7 @@ const lowerBoundDependencies = ( } const atomName = identifier(entry.identifier(1)); const atomId = requireSymbol(state, state.atoms, atomName, entry, "atom"); - return atomId - ? [{ portId, binding: { kind: "constructor", atomId } }] - : []; + return atomId ? [{ portId, binding: { kind: "constructor", atomId } }] : []; }); }; @@ -1237,15 +1025,10 @@ const lowerRelationshipMaterialization = ( if (edge && edge.attachment.kind !== "edge") { loweringIssue(state, context, "wrong-attachment-kind", `${edgeName} is state, not an edge`); } - const projectionId = edge?.attachment.kind === "edge" - ? requireSymbol( - state, - edge.projections, - identifier(context.identifier(3)), - context, - `projection on ${edgeName}`, - ) - : undefined; + const projectionId = + edge?.attachment.kind === "edge" + ? requireSymbol(state, edge.projections, identifier(context.identifier(3)), context, `projection on ${edgeName}`) + : undefined; return member && constructorAtom && edge?.attachment.kind === "edge" && projectionId ? { memberId: member.memberId, @@ -1264,149 +1047,99 @@ const lowerConformance = ( const atomName = identifier(context.identifier(0)); const interfaceName = identifier(context.identifier(1)); const atomId = requireSymbol(state, state.atoms, atomName, context, "atom"); - const interfaceSymbol = requireSymbol( - state, - state.interfaces, - interfaceName, - context, - "interface", - ); + const interfaceSymbol = requireSymbol(state, state.interfaces, interfaceName, context, "interface"); if (!atomId || !interfaceSymbol) { return undefined; } - const operationBindings = context.conformanceItem().flatMap< - WorkspaceRevision["conformances"][number]["operationBindings"][number] - >((item) => { - const bindingContext = item.operationBindingDecl(); - if (!bindingContext) { - return []; - } - const memberName = identifier( - bindingContext.memberOperationRef().identifier(), - ); - const operationName = text( - bindingContext.memberOperationRef()?.operationName() ?? null, - ); - const operationId = findOperationId( - state, - interfaceName, - memberName, - operationName, - bindingContext, - ); - if (!operationId) { - return []; - } - const provider = bindingContext.operationProvider(); - if (provider.STATE()) { - const attachmentName = identifier(provider.identifier(0)); - const attachment = requireSymbol( - state, - state.attachments, - attachmentName, - provider, - "attachment", - ); - if (!attachment || attachment.attachment.kind !== "state") { - if (attachment) { - loweringIssue( - state, - provider, - "wrong-attachment-kind", - `${attachmentName} is not state`, - ); - } + const operationBindings = context + .conformanceItem() + .flatMap((item) => { + const bindingContext = item.operationBindingDecl(); + if (!bindingContext) { return []; } - return [ - { - operationId, - binding: { - kind: "state" as const, - slotId: attachment.attachment.id, - primitive: text(provider.statePrimitive()) as StatePrimitive, + const memberName = identifier(bindingContext.memberOperationRef().identifier()); + const operationName = text(bindingContext.memberOperationRef()?.operationName() ?? null); + const operationId = findOperationId(state, interfaceName, memberName, operationName, bindingContext); + if (!operationId) { + return []; + } + const provider = bindingContext.operationProvider(); + if (provider.STATE()) { + const attachmentName = identifier(provider.identifier(0)); + const attachment = requireSymbol(state, state.attachments, attachmentName, provider, "attachment"); + if (!attachment || attachment.attachment.kind !== "state") { + if (attachment) { + loweringIssue(state, provider, "wrong-attachment-kind", `${attachmentName} is not state`); + } + return []; + } + return [ + { + operationId, + binding: { + kind: "state" as const, + slotId: attachment.attachment.id, + primitive: text(provider.statePrimitive()) as StatePrimitive, + }, }, - }, - ]; - } - if (provider.EDGE()) { - const edgeName = identifier(provider.identifier(0)); - const projectionName = identifier(provider.identifier(1)); - const attachment = requireSymbol( - state, - state.attachments, - edgeName, - provider, - "attachment", - ); - if (!attachment || attachment.attachment.kind !== "edge") { - if (attachment) { - loweringIssue( - state, - provider, - "wrong-attachment-kind", - `${edgeName} is not an edge`, - ); - } - return []; + ]; } - const projectionId = requireSymbol( - state, - attachment.projections, - projectionName, - provider, - `projection on ${edgeName}`, - ); - return projectionId + if (provider.EDGE()) { + const edgeName = identifier(provider.identifier(0)); + const projectionName = identifier(provider.identifier(1)); + const attachment = requireSymbol(state, state.attachments, edgeName, provider, "attachment"); + if (!attachment || attachment.attachment.kind !== "edge") { + if (attachment) { + loweringIssue(state, provider, "wrong-attachment-kind", `${edgeName} is not an edge`); + } + return []; + } + const projectionId = requireSymbol( + state, + attachment.projections, + projectionName, + provider, + `projection on ${edgeName}`, + ); + return projectionId + ? [ + { + operationId, + binding: { + kind: "edge" as const, + edgeTypeId: attachment.attachment.id, + projectionId, + primitive: text(provider.edgePrimitive()) as EdgePrimitive, + }, + }, + ] + : []; + } + const packageName = identifier(provider.identifier(0)); + const exportName = identifier(provider.identifier(1)); + const packageSymbol = requireSymbol(state, state.packages, packageName, provider, "package"); + const exportSymbol = packageSymbol + ? requireSymbol(state, packageSymbol.exports, exportName, provider, `export on ${packageName}`) + : undefined; + return packageSymbol && exportSymbol ? [ { operationId, binding: { - kind: "edge" as const, - edgeTypeId: attachment.attachment.id, - projectionId, - primitive: text(provider.edgePrimitive()) as EdgePrimitive, + kind: "package" as const, + packageRevisionId: packageSymbol.revisionId, + exportId: exportSymbol.exportId, + dependencies: lowerBoundDependencies( + state, + provider.dependencyBindingBlock() ?? undefined, + exportSymbol, + ), }, }, ] : []; - } - const packageName = identifier(provider.identifier(0)); - const exportName = identifier(provider.identifier(1)); - const packageSymbol = requireSymbol( - state, - state.packages, - packageName, - provider, - "package", - ); - const exportSymbol = packageSymbol - ? requireSymbol( - state, - packageSymbol.exports, - exportName, - provider, - `export on ${packageName}`, - ) - : undefined; - return packageSymbol && exportSymbol - ? [ - { - operationId, - binding: { - kind: "package" as const, - packageRevisionId: packageSymbol.revisionId, - exportId: exportSymbol.exportId, - dependencies: lowerBoundDependencies( - state, - provider.dependencyBindingBlock() ?? undefined, - exportSymbol, - ), - }, - }, - ] - : []; - }); + }); const relationshipMaterializations = context.conformanceItem().flatMap((item) => { const materialization = item.relationshipMaterializationDecl(); if (!materialization) return []; @@ -1427,35 +1160,31 @@ 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, - ])), - }, - ])), + 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, - ])), - }, - ])), + 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 => ({ +const resourceImport = (context: ResourceImportDeclContext): CapabilityResourceImport => ({ kind: context.INTERFACE() ? "interface" : "package", binding: identifier(context.identifier()), }); @@ -1464,54 +1193,45 @@ 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}`, - ); +) => + 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 { - declareSymbol( - state, - state.interfaces, - imported.binding, - interfaceSymbolFor(revision), - context, - "interface", - ); + 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"); + } } - } 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; -}); + return imported; + }); -const uniqueExactRevisions = (revisions: readonly Revision[]): Revision[] => { +const uniqueExactRevisions = < + Revision extends { + revisionId: string; + source: SourceRevision; + }, +>( + revisions: readonly Revision[], +): Revision[] => { const seen = new Set(); return revisions.filter((revision) => { const key = `${revision.revisionId}\0${revision.source.repository}\0${revision.source.commit}`; @@ -1569,9 +1289,7 @@ const lowerWorkspace = ( { id: atomId, displayName: identifier(atom.identifier()), - documentation: atom.DOC() - ? stringValue(atom.stringLiteral(1)) - : undefined, + documentation: atom.DOC() ? stringValue(atom.stringLiteral(1)) : undefined, }, ] : []; @@ -1588,10 +1306,7 @@ const lowerWorkspace = ( ]); const sharedAttachments: PersistentAttachment[] = []; - const privateAttachments = new Map< - ConformanceDeclContext, - PersistentAttachment[] - >(); + const privateAttachments = new Map(); for (const item of items) { const shared = item.sharedAttachmentDecl(); if (shared) { @@ -1625,11 +1340,7 @@ const lowerWorkspace = ( if (!context) { return []; } - const conformance = lowerConformance( - state, - context, - privateAttachments.get(context) ?? [], - ); + const conformance = lowerConformance(state, context, privateAttachments.get(context) ?? []); return conformance ? [conformance] : []; }); @@ -1642,21 +1353,9 @@ const lowerWorkspace = ( const packageName = identifier(constructor.identifier(1)); const exportName = identifier(constructor.identifier(2)); const atomId = requireSymbol(state, state.atoms, atomName, constructor, "atom"); - const packageSymbol = requireSymbol( - state, - state.packages, - packageName, - constructor, - "package", - ); + const packageSymbol = requireSymbol(state, state.packages, packageName, constructor, "package"); const exportSymbol = packageSymbol - ? requireSymbol( - state, - packageSymbol.exports, - exportName, - constructor, - `export on ${packageName}`, - ) + ? requireSymbol(state, packageSymbol.exports, exportName, constructor, `export on ${packageName}`) : undefined; return atomId && packageSymbol && exportSymbol ? [ @@ -1709,10 +1408,7 @@ export const parseDocument = ( return { tree, tokens, diagnostics }; }; -const newLoweringState = ( - fileName: string, - diagnostics: CapabilitySourceDiagnostic[], -): LoweringState => ({ +const newLoweringState = (fileName: string, diagnostics: CapabilitySourceDiagnostic[]): LoweringState => ({ fileName, diagnostics, atoms: new Map(), @@ -1726,57 +1422,50 @@ const validationDiagnostics = ( issues: ReturnType 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, -})); +): 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 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; -}); +): 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() ?? []), @@ -1793,41 +1482,40 @@ const resourceValidationWorkspace = ( ...[...(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: [], - }); + 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 = ( @@ -1850,21 +1538,21 @@ export const compileCapabilityResourceSource = ( 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, - }], + 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 preamble = resourcePreambleParts((interfaceContext ?? packageContext)!.resourcePreamble()); const externalAtoms = externalAtomsFrom(state, preamble.atoms); const imports = registerImports(state, preamble.imports, environment); const externalInterfaces = externalInterfacesFrom(state, preamble.interfaces); @@ -1877,9 +1565,7 @@ export const compileCapabilityResourceSource = ( state.interfaces, alias, { - revisionId: capabilityId.interfaceRevision( - stringValue(interfaceContext.stringLiteral(1)), - ), + revisionId: capabilityId.interfaceRevision(stringValue(interfaceContext.stringLiteral(1))), contractAvailable: true, members: new Map(), }, @@ -1901,9 +1587,7 @@ export const compileCapabilityResourceSource = ( state.packages, alias, { - revisionId: capabilityId.packageRevision( - stringValue(context.stringLiteral(1)), - ), + revisionId: capabilityId.packageRevision(stringValue(context.stringLiteral(1))), exports: new Map(), }, context, @@ -1921,9 +1605,7 @@ export const compileCapabilityResourceSource = ( if (diagnostics.length > 0) { return { ok: false, diagnostics }; } - const compiled = compileWorkspaceRevision( - resourceValidationWorkspace(resource, environment), - ); + const compiled = compileWorkspaceRevision(resourceValidationWorkspace(resource, environment)); if (!compiled.ok) { return { ok: false, @@ -1946,21 +1628,23 @@ export const compileCapabilitySource = ( if (!workspaceContext) { return { ok: false, - diagnostics: [{ - phase: "lowering", - code: "expected-workspace", - message: "Expected a workspace document", - fileName, - line: 1, - column: 0, - }], + diagnostics: [ + { + phase: "lowering", + code: "expected-workspace", + message: "Expected a workspace document", + fileName, + line: 1, + column: 0, + }, + ], }; } const state = newLoweringState(fileName, diagnostics); for (const item of workspaceContext.workspaceItem()) { - if (item.sourceImportDecl()) loweringIssue(state, item, "unresolved-source-import", - "Local imports require the workspace repository compiler"); + if (item.sourceImportDecl()) + loweringIssue(state, item, "unresolved-source-import", "Local imports require the workspace repository compiler"); } if (diagnostics.length) return { ok: false, diagnostics }; const lowered = lowerWorkspace(state, workspaceContext, environment); diff --git a/src/capability-language/pin-upgrades.ts b/src/capability-language/pin-upgrades.ts index 927313b..5748d98 100644 --- a/src/capability-language/pin-upgrades.ts +++ b/src/capability-language/pin-upgrades.ts @@ -1,100 +1,185 @@ import fs from "node:fs/promises"; import path from "node:path"; import os from "node:os"; -import {randomUUID} from "node:crypto"; -import {execFile as callback} from "node:child_process"; -import {promisify} from "node:util"; -import {loadQuixosLock, parseQuixosLockDocument} from "../resource-lock/index.js"; -import {contentDigest} from "../capability-model/evolution.js"; -import {planStructure, applyStructure, type StructuralRequest} from "./structural-plan.js"; -import {snapshotRepository} from "./candidate-check.js"; -import {compileWorkspaceRepository, compileCapabilityResourceRepository, type ResolvedCapabilityResource} from "./assembly.js"; -import {createGitCapabilityResolver} from "./git-resolver.js"; -import {snapshotCommit, buildImmutableCandidate} from "./checked-build.js"; -import {planEvolution, type WorkspaceRevision, type EvolutionReview} from "../capability-model/index.js"; -import {withFileLock} from "./file-lock.js"; +import { randomUUID } from "node:crypto"; +import { execFile as callback } from "node:child_process"; +import { promisify } from "node:util"; +import { loadQuixosLock, parseQuixosLockDocument } from "../resource-lock/index.js"; +import { contentDigest } from "../capability-model/evolution.js"; +import { planStructure, applyStructure, type StructuralRequest } from "./structural-plan.js"; +import { snapshotRepository } from "./candidate-check.js"; +import { + compileWorkspaceRepository, + compileCapabilityResourceRepository, + type ResolvedCapabilityResource, +} from "./assembly.js"; +import { createGitCapabilityResolver } from "./git-resolver.js"; +import { snapshotCommit, buildImmutableCandidate } from "./checked-build.js"; +import { planEvolution, type WorkspaceRevision, type EvolutionReview } from "../capability-model/index.js"; +import { withFileLock } from "./file-lock.js"; const execFile = promisify(callback); -type Source = {repository: string; commit: string}; -export type UpgradeNode = {kind: "workspace" | "package" | "interface"; directory: string; source: Source}; -export type UpgradeSpec = {nodes: UpgradeNode[]; quixos?: Source; baseline?: string; reviews?: string; bootstrap?: boolean}; -type NodePlan = UpgradeNode & {treeDigest: string; dependencies: string[]; lockFiles: string[]}; -export type UpgradePlan = {schemaVersion: 1; workbench: string; spec: UpgradeSpec; nodes: NodePlan[]; digest: string}; -type Step = {directory: string; phase: "editing" | "prepared" | "refactor" | "checked" | "publishing" | "published"; commit?: string; treeDigest?: string; structuralJournal?: string; structuralPlan?: Awaited>}; -type Journal = {schemaVersion: 1; plan: UpgradePlan; steps: Step[]}; -const sourceKey = (node: {kind: string; source: Source}) => JSON.stringify([node.kind, node.source.repository, node.source.commit]); -const command = async (cwd: string, tool: string, args: string[]) => (await execFile(tool, args, {cwd, maxBuffer: 16 * 1024 * 1024, env: {...process.env, GIT_TERMINAL_PROMPT: "0", QUIXOS_JJ_NO_CHECKPOINT: "1", QUIXOS_SUBTREE_PUBLISH: "0"}})).stdout.trim(); +type Source = { repository: string; commit: string }; +export type UpgradeNode = { kind: "workspace" | "package" | "interface"; directory: string; source: Source }; +export type UpgradeSpec = { + nodes: UpgradeNode[]; + quixos?: Source; + baseline?: string; + reviews?: string; + bootstrap?: boolean; +}; +type NodePlan = UpgradeNode & { treeDigest: string; dependencies: string[]; lockFiles: string[] }; +export type UpgradePlan = { schemaVersion: 1; workbench: string; spec: UpgradeSpec; nodes: NodePlan[]; digest: string }; +type Step = { + directory: string; + phase: "editing" | "prepared" | "refactor" | "checked" | "publishing" | "published"; + commit?: string; + treeDigest?: string; + structuralJournal?: string; + structuralPlan?: Awaited>; +}; +type Journal = { schemaVersion: 1; plan: UpgradePlan; steps: Step[] }; +const sourceKey = (node: { kind: string; source: Source }) => + JSON.stringify([node.kind, node.source.repository, node.source.commit]); +const command = async (cwd: string, tool: string, args: string[]) => + ( + await execFile(tool, args, { + cwd, + maxBuffer: 16 * 1024 * 1024, + env: { ...process.env, GIT_TERMINAL_PROMPT: "0", QUIXOS_JJ_NO_CHECKPOINT: "1", QUIXOS_SUBTREE_PUBLISH: "0" }, + }) + ).stdout.trim(); const validSource = (value: Source) => { const url = new URL(value.repository); - if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash || !/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(value.commit)) throw new Error("Upgrade sources must be exact credential-free HTTPS revisions"); + if ( + url.protocol !== "https:" || + url.username || + url.password || + url.search || + url.hash || + !/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(value.commit) + ) + throw new Error("Upgrade sources must be exact credential-free HTTPS revisions"); }; const location = async (root: string, directory: string) => { - if (directory !== "root" && !/^resources\/[A-Za-z0-9][A-Za-z0-9._-]*$/.test(directory)) throw new Error("Upgrade target must be a managed root/resource repository"); + if (directory !== "root" && !/^resources\/[A-Za-z0-9][A-Za-z0-9._-]*$/.test(directory)) + throw new Error("Upgrade target must be a managed root/resource repository"); const resolved = await fs.realpath(path.join(root, directory)); if (resolved !== path.join(root, directory)) throw new Error("Upgrade target crosses a symlink"); return resolved; }; const treeDigest = async (root: string) => { const temporary = await fs.mkdtemp(path.join(os.tmpdir(), "qx-upgrade-tree-")); - try {return (await snapshotRepository(root, temporary)).treeDigest;} finally {await fs.rm(temporary, {recursive: true, force: true});} + try { + return (await snapshotRepository(root, temporary)).treeDigest; + } finally { + await fs.rm(temporary, { recursive: true, force: true }); + } }; const writeJournal = async (filename: string, journal: unknown) => { const temp = `${filename}.${randomUUID()}.tmp`; const handle = await fs.open(temp, "wx", 0o600); - try {await handle.writeFile(JSON.stringify(journal, null, 2)); await handle.sync();} finally {await handle.close();} + try { + await handle.writeFile(JSON.stringify(journal, null, 2)); + await handle.sync(); + } finally { + await handle.close(); + } await fs.rename(temp, filename); const directory = await fs.open(path.dirname(filename), "r"); - try {await directory.sync();} finally {await directory.close();} + try { + await directory.sync(); + } finally { + await directory.close(); + } }; export const discoverUpgradeSpec = async (workbench: string): Promise => { const graph = JSON.parse(await fs.readFile(path.join(workbench, ".quixos/resource-graph.json"), "utf8")); const root = await location(workbench, "root"); - const nodes: UpgradeNode[] = [{kind: "workspace", directory: "root", source: { - repository: await command(root, "git", ["config", "--get", "remote.origin.url"]), - commit: await command(root, "jj", ["--ignore-working-copy", "log", "--no-graph", "-r", "@", "-T", "commit_id"]), - }}, ...graph.resources.map((entry: {kind: "interface" | "package"; directory: string; source: Source}) => ({kind: entry.kind, source: entry.source, - directory: path.relative(workbench, path.resolve(workbench, entry.directory))}))]; + const nodes: UpgradeNode[] = [ + { + kind: "workspace", + directory: "root", + source: { + repository: await command(root, "git", ["config", "--get", "remote.origin.url"]), + commit: await command(root, "jj", ["--ignore-working-copy", "log", "--no-graph", "-r", "@", "-T", "commit_id"]), + }, + }, + ...graph.resources.map((entry: { kind: "interface" | "package"; directory: string; source: Source }) => ({ + kind: entry.kind, + source: entry.source, + directory: path.relative(workbench, path.resolve(workbench, entry.directory)), + })), + ]; let baseline: string | undefined; try { const host = JSON.parse(await fs.readFile("/etc/quixos/workspace-source.json", "utf8")); - if (await fs.realpath(host.workbenchRoot) === await fs.realpath(workbench)) baseline = JSON.parse(await fs.readFile(path.join(host.runtimeClosureRoot, "manifest.json"), "utf8")).workspacePlanPath; - } catch (error) {if (!["ENOENT", "EACCES"].includes((error as NodeJS.ErrnoException).code ?? "")) throw error;} - return {nodes, baseline}; + if ((await fs.realpath(host.workbenchRoot)) === (await fs.realpath(workbench))) + baseline = JSON.parse( + await fs.readFile(path.join(host.runtimeClosureRoot, "manifest.json"), "utf8"), + ).workspacePlanPath; + } catch (error) { + if (!["ENOENT", "EACCES"].includes((error as NodeJS.ErrnoException).code ?? "")) throw error; + } + return { nodes, baseline }; }; /** Read-only source plan. Repositories are selected explicitly, including any * parallel versions of the same resource; no guesses at a floating 'latest'. */ export const planPinUpgrades = async (workbenchPath: string, spec: UpgradeSpec): Promise => { const workbench = await fs.realpath(workbenchPath); - if (!Array.isArray(spec.nodes) || !spec.nodes.length || spec.nodes.length > 100 || spec.nodes.filter((node) => node.kind === "workspace").length !== 1) throw new Error("Upgrade graph requires one workspace and at most 100 repositories"); + if ( + !Array.isArray(spec.nodes) || + !spec.nodes.length || + spec.nodes.length > 100 || + spec.nodes.filter((node) => node.kind === "workspace").length !== 1 + ) + throw new Error("Upgrade graph requires one workspace and at most 100 repositories"); if (spec.quixos) validSource(spec.quixos); const keys = new Map(); for (const node of spec.nodes) { validSource(node.source); - if (!["workspace", "package", "interface"].includes(node.kind) || keys.has(sourceKey(node))) throw new Error("Duplicate/invalid upgrade resource identity"); + if (!["workspace", "package", "interface"].includes(node.kind) || keys.has(sourceKey(node))) + throw new Error("Duplicate/invalid upgrade resource identity"); keys.set(sourceKey(node), node.directory); } - if (new Set(spec.nodes.map((node) => node.directory)).size !== spec.nodes.length) throw new Error("Upgrade directories must be distinct"); + if (new Set(spec.nodes.map((node) => node.directory)).size !== spec.nodes.length) + throw new Error("Upgrade directories must be distinct"); const nodes: NodePlan[] = []; for (const node of spec.nodes) { const root = await location(workbench, node.directory); - if (await command(root, "git", ["config", "--get", "remote.origin.url"]) !== node.source.repository) throw new Error(`Upgrade origin differs from selected source: ${node.directory}`); + if ((await command(root, "git", ["config", "--get", "remote.origin.url"])) !== node.source.repository) + throw new Error(`Upgrade origin differs from selected source: ${node.directory}`); const loaded = await loadQuixosLock(path.join(root, "quixos.lock")); - if (!loaded.ok) throw new Error(`Invalid lock in ${node.directory}: ${loaded.diagnostics.map((entry) => entry.message).join("; ")}`); - const dependencies = loaded.lock.resources.map((resource) => keys.get(sourceKey(resource))).filter((value): value is string => Boolean(value)); - nodes.push({...node, treeDigest: await treeDigest(root), dependencies: [...new Set(dependencies)], lockFiles: loaded.lock.sourceFiles ?? ["quixos.lock"]}); + if (!loaded.ok) + throw new Error( + `Invalid lock in ${node.directory}: ${loaded.diagnostics.map((entry) => entry.message).join("; ")}`, + ); + const dependencies = loaded.lock.resources + .map((resource) => keys.get(sourceKey(resource))) + .filter((value): value is string => Boolean(value)); + nodes.push({ + ...node, + treeDigest: await treeDigest(root), + dependencies: [...new Set(dependencies)], + lockFiles: loaded.lock.sourceFiles ?? ["quixos.lock"], + }); } - const ordered: NodePlan[] = [], remaining = [...nodes]; + const ordered: NodePlan[] = [], + remaining = [...nodes]; while (remaining.length) { - const index = remaining.findIndex((node) => node.dependencies.every((dependency) => ordered.some((entry) => entry.directory === dependency))); + const index = remaining.findIndex((node) => + node.dependencies.every((dependency) => ordered.some((entry) => entry.directory === dependency)), + ); if (index < 0) throw new Error("Cyclic source publication graph"); ordered.push(remaining.splice(index, 1)[0]); } const workspace = ordered.find((node) => node.kind === "workspace")!; // Even unreferenced new resources are published before the root. - ordered.splice(ordered.indexOf(workspace), 1); ordered.push(workspace); - const plan = {schemaVersion: 1 as const, workbench, spec, nodes: ordered}; - return {...plan, digest: contentDigest(plan)}; + ordered.splice(ordered.indexOf(workspace), 1); + ordered.push(workspace); + const plan = { schemaVersion: 1 as const, workbench, spec, nodes: ordered }; + return { ...plan, digest: contentDigest(plan) }; }; export type UpgradeEffects = { @@ -104,21 +189,27 @@ export type UpgradeEffects = { }; const effects: UpgradeEffects = { async check(node, root, output, spec) { - if (node.kind === "workspace" && !spec.baseline && !spec.bootstrap) throw new Error("Upgrading a workspace requires its checked active baseline for major-review checks (or explicit bootstrap:true for a new workspace)"); + if (node.kind === "workspace" && !spec.baseline && !spec.bootstrap) + throw new Error( + "Upgrading a workspace requires its checked active baseline for major-review checks (or explicit bootstrap:true for a new workspace)", + ); // Explicit baseline upgrades use the same immutable Nix checker. Retaining // an unverified source is safe and must precede a remote flake fetch. await fs.mkdir(output); const commit = await snapshotCommit(root); await effects.publish(root, commit); - const artifact = await buildImmutableCandidate({...node.source, commit}, node.kind, path.join(output, "nix.log")); + const artifact = await buildImmutableCandidate({ ...node.source, commit }, node.kind, path.join(output, "nix.log")); const candidate = await fs.readFile(path.join(artifact, "candidate.json"), "utf8"); await fs.writeFile(path.join(output, "candidate.json"), candidate); if (node.kind === "workspace") { - const baseline = spec.baseline ? JSON.parse(await fs.readFile(spec.baseline, "utf8")) as WorkspaceRevision : null; - const reviews = spec.reviews ? JSON.parse(await fs.readFile(spec.reviews, "utf8")) as EvolutionReview[] : []; - const evolution = planEvolution(baseline, JSON.parse(candidate), {reviews}); + const baseline = spec.baseline + ? (JSON.parse(await fs.readFile(spec.baseline, "utf8")) as WorkspaceRevision) + : null; + const reviews = spec.reviews ? (JSON.parse(await fs.readFile(spec.reviews, "utf8")) as EvolutionReview[]) : []; + const evolution = planEvolution(baseline, JSON.parse(candidate), { reviews }); await fs.writeFile(path.join(output, "evolution.json"), JSON.stringify(evolution, null, 2)); - if (evolution.blockers.length) throw new Error(`Refactor required in ${node.directory}: ${evolution.blockers.join("; ")}`); + if (evolution.blockers.length) + throw new Error(`Refactor required in ${node.directory}: ${evolution.blockers.join("; ")}`); } }, async snapshot(root) { @@ -126,7 +217,10 @@ const effects: UpgradeEffects = { if (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(commit)) throw new Error("Publication did not resolve an exact commit"); await command(root, "git", ["diff", "--exit-code", "--no-ext-diff", "--no-textconv", commit, "--"]); const tracked = new Set((await command(root, "git", ["ls-tree", "-r", "--name-only", "-z", commit])).split("\0")); - for (const file of (await command(root, "git", ["ls-files", "--others", "--exclude-standard", "-z"])).split("\0").filter(Boolean)) if (!tracked.has(file)) throw new Error(`Uncaptured source file ${file}`); + for (const file of (await command(root, "git", ["ls-files", "--others", "--exclude-standard", "-z"])) + .split("\0") + .filter(Boolean)) + if (!tracked.has(file)) throw new Error(`Uncaptured source file ${file}`); return commit; }, async publish(root, commit) { @@ -134,121 +228,250 @@ const effects: UpgradeEffects = { const remote = await command(root, "git", ["ls-remote", "--refs", "origin", ref]); if (remote && remote !== `${commit}\t${ref}`) throw new Error("Immutable publication ref conflict"); if (!remote) await command(root, "git", ["push", "origin", `${commit}:${ref}`]); - if (await command(root, "git", ["ls-remote", "--refs", "origin", ref]) !== `${commit}\t${ref}`) throw new Error("Publication response uncertain; retry the same journal"); + if ((await command(root, "git", ["ls-remote", "--refs", "origin", ref])) !== `${commit}\t${ref}`) + throw new Error("Publication response uncertain; retry the same journal"); }, }; /** Explicit --publish only. Append-only remote retention; never moves the * workspace branch, activates code, or rolls back previously published nodes. */ -export const applyPinUpgrades = async (plan: UpgradePlan, journalId?: string, implementation: UpgradeEffects = effects, options: {acceptEdits?: boolean} = {}) => { - if (implementation === effects && !plan.spec.baseline && !plan.spec.bootstrap) throw new Error("Publication requires an active checked baseline or explicit bootstrap:true"); - const {digest, ...body} = plan; +export const applyPinUpgrades = async ( + plan: UpgradePlan, + journalId?: string, + implementation: UpgradeEffects = effects, + options: { acceptEdits?: boolean } = {}, +) => { + if (implementation === effects && !plan.spec.baseline && !plan.spec.bootstrap) + throw new Error("Publication requires an active checked baseline or explicit bootstrap:true"); + const { digest, ...body } = plan; if (contentDigest(body) !== digest) throw new Error("Upgrade plan digest mismatch"); const directory = path.join(plan.workbench, ".quixos", "upgrades"); - await fs.mkdir(directory, {recursive: true, mode: 0o700}); - if (await fs.realpath(directory) !== directory) throw new Error("Upgrade journals must not cross symlinks"); + await fs.mkdir(directory, { recursive: true, mode: 0o700 }); + if ((await fs.realpath(directory)) !== directory) throw new Error("Upgrade journals must not cross symlinks"); const id = journalId ?? randomUUID(); if (!/^[a-f0-9-]{36}$/.test(id)) throw new Error("Invalid upgrade journal ID"); const filename = path.join(directory, `${id}.json`); return withFileLock(path.join(directory, "writer.lock"), async () => { - try { - const journal: Journal = journalId ? JSON.parse(await fs.readFile(filename, "utf8")) : {schemaVersion: 1, plan, steps: []}; - if (journal.plan.digest !== plan.digest) throw new Error("Upgrade journal belongs to another plan"); - if (!journalId) await writeJournal(filename, journal); - for (const node of plan.nodes) { - const root = await location(plan.workbench, node.directory); - if (await command(root, "git", ["config", "--get", "remote.origin.url"]) !== node.source.repository) throw new Error("Upgrade remote changed after planning"); - let step = journal.steps.find((entry) => entry.directory === node.directory); - if (step?.phase === "published") continue; - if (!step) { - if (await treeDigest(root) !== node.treeDigest) throw new Error(`Stale upgrade plan: ${node.directory}`); - const files: StructuralRequest["files"] = []; - for (const file of node.lockFiles) { - const parsed = parseQuixosLockDocument(await fs.readFile(path.join(root, file), "utf8")); - if (!parsed.ok) throw new Error("Invalid lock during upgrade"); - const edits = []; - for (const resource of parsed.document.resources) { - const dependency = plan.nodes.find((entry) => sourceKey(entry) === sourceKey(resource)); - const published = dependency && journal.steps.find((entry) => entry.directory === dependency.directory && entry.phase === "published"); - if (published?.commit && published.commit !== resource.source.commit) edits.push({operation: "dependency" as const, kind: resource.kind, name: resource.binding, source: {...resource.source, commit: published.commit}}); + try { + const journal: Journal = journalId + ? JSON.parse(await fs.readFile(filename, "utf8")) + : { schemaVersion: 1, plan, steps: [] }; + if (journal.plan.digest !== plan.digest) throw new Error("Upgrade journal belongs to another plan"); + if (!journalId) await writeJournal(filename, journal); + for (const node of plan.nodes) { + const root = await location(plan.workbench, node.directory); + if ((await command(root, "git", ["config", "--get", "remote.origin.url"])) !== node.source.repository) + throw new Error("Upgrade remote changed after planning"); + let step = journal.steps.find((entry) => entry.directory === node.directory); + if (step?.phase === "published") continue; + if (!step) { + if ((await treeDigest(root)) !== node.treeDigest) throw new Error(`Stale upgrade plan: ${node.directory}`); + const files: StructuralRequest["files"] = []; + for (const file of node.lockFiles) { + const parsed = parseQuixosLockDocument(await fs.readFile(path.join(root, file), "utf8")); + if (!parsed.ok) throw new Error("Invalid lock during upgrade"); + const edits = []; + for (const resource of parsed.document.resources) { + const dependency = plan.nodes.find((entry) => sourceKey(entry) === sourceKey(resource)); + const published = + dependency && + journal.steps.find((entry) => entry.directory === dependency.directory && entry.phase === "published"); + if (published?.commit && published.commit !== resource.source.commit) + edits.push({ + operation: "dependency" as const, + kind: resource.kind, + name: resource.binding, + source: { ...resource.source, commit: published.commit }, + }); + } + if (parsed.document.kind === "root" && plan.spec.quixos) + edits.push({ operation: "quixos-pin" as const, source: plan.spec.quixos }); + if (edits.length) files.push({ file, edits }); } - if (parsed.document.kind === "root" && plan.spec.quixos) edits.push({operation: "quixos-pin" as const, source: plan.spec.quixos}); - if (edits.length) files.push({file, edits}); + const structuralPlan = files.length + ? await planStructure( + root, + { kind: node.kind, source: node.source, files }, + process.env.QUIXOS_SNAPSHOT_MAP, + ) + : undefined; + step = { + directory: node.directory, + phase: "editing", + treeDigest: node.treeDigest, + structuralPlan, + structuralJournal: structuralPlan ? randomUUID() : undefined, + }; + journal.steps.push(step); + await writeJournal(filename, journal); } - const structuralPlan = files.length ? await planStructure(root, {kind: node.kind, source: node.source, files}, process.env.QUIXOS_SNAPSHOT_MAP) : undefined; - step = {directory: node.directory, phase: "editing", treeDigest: node.treeDigest, structuralPlan, structuralJournal: structuralPlan ? randomUUID() : undefined}; - journal.steps.push(step); await writeJournal(filename, journal); - } - if (step.phase === "editing") { - if (step.structuralPlan) await applyStructure(step.structuralPlan, step.structuralJournal); - else if (await treeDigest(root) !== step.treeDigest) throw new Error("Source changed before upgrade editing"); - step.treeDigest = await treeDigest(root); - step.phase = "prepared"; + if (step.phase === "editing") { + if (step.structuralPlan) await applyStructure(step.structuralPlan, step.structuralJournal); + else if ((await treeDigest(root)) !== step.treeDigest) + throw new Error("Source changed before upgrade editing"); + step.treeDigest = await treeDigest(root); + step.phase = "prepared"; + await writeJournal(filename, journal); + } + if (step.phase === "refactor") { + const current = await treeDigest(root); + if (current !== step.treeDigest && !options.acceptEdits) + throw new Error("Refactored source requires --accept-edits when resuming"); + step.treeDigest = current; + step.phase = "prepared"; + await writeJournal(filename, journal); + } + if ((await treeDigest(root)) !== step.treeDigest) + throw new Error(`Source changed during upgrade: ${node.directory}; inspect ${filename}`); + if (step.phase === "prepared") { + try { + await implementation.check( + node, + root, + path.join(directory, `${id}-${node.directory.replaceAll("/", "-")}-${randomUUID()}`), + plan.spec, + ); + } catch (error) { + step.phase = "refactor"; + await writeJournal(filename, journal); + throw error; + } + if ((await treeDigest(root)) !== step.treeDigest) throw new Error("Source changed while checking"); + step.phase = "checked"; + await writeJournal(filename, journal); + } + if (step.phase === "checked") { + step.commit = await implementation.snapshot(root); + if ((await treeDigest(root)) !== step.treeDigest) + throw new Error("Publication snapshot changed checked files"); + step.phase = "publishing"; + await writeJournal(filename, journal); + } + await implementation.publish(root, step.commit!); + step.phase = "published"; await writeJournal(filename, journal); } - if (step.phase === "refactor") { - const current = await treeDigest(root); - if (current !== step.treeDigest && !options.acceptEdits) throw new Error("Refactored source requires --accept-edits when resuming"); - step.treeDigest = current; step.phase = "prepared"; await writeJournal(filename, journal); + // Keep subsequent automatic upgrades associated with the newly published + // identities, without renaming repositories or changing any selected branch. + // Explicit-spec callers without a managed graph retain the journal as their + // source of revisions instead. + const graphFile = path.join(plan.workbench, ".quixos/resource-graph.json"); + let graphText: string | undefined; + try { + graphText = await fs.readFile(graphFile, "utf8"); + } catch (error) { + if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; } - if (await treeDigest(root) !== step.treeDigest) throw new Error(`Source changed during upgrade: ${node.directory}; inspect ${filename}`); - if (step.phase === "prepared") { - try {await implementation.check(node, root, path.join(directory, `${id}-${node.directory.replaceAll("/", "-")}-${randomUUID()}`), plan.spec);} - catch (error) {step.phase = "refactor"; await writeJournal(filename, journal); throw error;} - if (await treeDigest(root) !== step.treeDigest) throw new Error("Source changed while checking"); - step.phase = "checked"; await writeJournal(filename, journal); + if (graphText !== undefined) { + const previous = JSON.parse(graphText); + const snapshots = await Promise.all( + previous.resources.map(async (entry: { kind: string; source: Source; directory: string }) => ({ + kind: entry.kind, + ...entry.source, + directory: await location( + plan.workbench, + path.relative(plan.workbench, path.resolve(plan.workbench, entry.directory)), + ), + })), + ); + for (const node of plan.nodes.filter((entry) => entry.kind !== "workspace")) { + const step = journal.steps.find((entry) => entry.directory === node.directory)!; + if ((await treeDigest(await location(plan.workbench, node.directory))) !== step.treeDigest) + throw new Error("Published source changed before workbench graph refresh"); + snapshots.push({ + kind: node.kind, + repository: node.source.repository, + commit: step.commit, + directory: path.join(plan.workbench, node.directory), + }); + } + const unique = [ + ...new Map( + snapshots.map((entry: { kind: string; repository: string; commit: string }) => [ + JSON.stringify([entry.kind, entry.repository, entry.commit]), + entry, + ]), + ).values(), + ]; + const snapshotMap = path.join(directory, `${id}-published-snapshots.json`); + await fs.writeFile(snapshotMap, JSON.stringify({ resources: unique })); + const resolveResource = await createGitCapabilityResolver({ + checkoutRoot: path.join(directory, `${id}-graph-resources`), + snapshotMap, + snapshotOnly: true, + }); + const rootNode = plan.nodes.find((node) => node.kind === "workspace")!; + if ( + (await treeDigest(await location(plan.workbench, rootNode.directory))) !== + journal.steps.find((step) => step.directory === rootNode.directory)!.treeDigest + ) + throw new Error("Root source changed before workbench graph refresh"); + const compiled = await compileWorkspaceRepository({ + rootDirectory: await location(plan.workbench, rootNode.directory), + resolveResource, + }); + // Managed repositories need not currently be reachable from the workspace. + // Keep them discoverable/checkpointed until explicitly removed by the user. + const resources = new Map( + compiled.resources.map((node) => [node.key, node]), + ); + for (const node of plan.nodes.filter((entry) => entry.kind !== "workspace")) { + const step = journal.steps.find((entry) => entry.directory === node.directory)!; + const source = { resolver: "git" as const, repository: node.source.repository, commit: step.commit! }; + const key = `${node.kind}\0${source.repository}\0${source.commit}`; + if (resources.has(key)) continue; + const directory = await location(plan.workbench, node.directory); + const standalone = await compileCapabilityResourceRepository({ + rootDirectory: directory, + kind: node.kind as "package" | "interface", + source, + resolveResource, + }); + for (const dependency of standalone.resources) resources.set(dependency.key, dependency); + resources.set(key, { + key, + kind: node.kind as "package" | "interface", + source, + directory, + lock: standalone.lock, + resource: standalone.resource, + dependencies: standalone.directResources, + }); + } + await writeJournal(graphFile, { + formatVersion: 1, + quixos: compiled.lock.quixos, + directResources: [...compiled.directResources.entries()].map(([bindingKey, node]) => { + const [kind, binding] = bindingKey.split("\0"); + return { kind, binding, resourceKey: node.key, directory: node.directory }; + }), + resources: [...resources.values()].map((node) => ({ + key: node.key, + kind: node.kind, + source: node.source, + directory: node.directory, + resourceId: + node.resource.kind === "interface" + ? node.resource.revision.interfaceId + : node.resource.revision.packageId, + revisionId: node.resource.revision.revisionId, + dependencies: [...node.dependencies.entries()].map(([binding, dependency]) => ({ + binding, + resourceKey: dependency.key, + })), + })), + }); } - if (step.phase === "checked") { - step.commit = await implementation.snapshot(root); - if (await treeDigest(root) !== step.treeDigest) throw new Error("Publication snapshot changed checked files"); - step.phase = "publishing"; await writeJournal(filename, journal); - } - await implementation.publish(root, step.commit!); - step.phase = "published"; await writeJournal(filename, journal); - } - // Keep subsequent automatic upgrades associated with the newly published - // identities, without renaming repositories or changing any selected branch. - // Explicit-spec callers without a managed graph retain the journal as their - // source of revisions instead. - const graphFile = path.join(plan.workbench, ".quixos/resource-graph.json"); - let graphText: string | undefined; - try {graphText = await fs.readFile(graphFile, "utf8");} catch (error) {if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;} - if (graphText !== undefined) { - const previous = JSON.parse(graphText); - const snapshots = await Promise.all(previous.resources.map(async (entry: {kind: string; source: Source; directory: string}) => ({kind: entry.kind, ...entry.source, directory: await location(plan.workbench, path.relative(plan.workbench, path.resolve(plan.workbench, entry.directory)))}))); - for (const node of plan.nodes.filter((entry) => entry.kind !== "workspace")) { - const step = journal.steps.find((entry) => entry.directory === node.directory)!; - if (await treeDigest(await location(plan.workbench, node.directory)) !== step.treeDigest) throw new Error("Published source changed before workbench graph refresh"); - snapshots.push({kind: node.kind, repository: node.source.repository, commit: step.commit, directory: path.join(plan.workbench, node.directory)}); - } - const unique = [...new Map(snapshots.map((entry: {kind: string; repository: string; commit: string}) => [JSON.stringify([entry.kind, entry.repository, entry.commit]), entry])).values()]; - const snapshotMap = path.join(directory, `${id}-published-snapshots.json`); - await fs.writeFile(snapshotMap, JSON.stringify({resources: unique})); - const resolveResource = await createGitCapabilityResolver({checkoutRoot: path.join(directory, `${id}-graph-resources`), snapshotMap, snapshotOnly: true}); - const rootNode = plan.nodes.find((node) => node.kind === "workspace")!; - if (await treeDigest(await location(plan.workbench, rootNode.directory)) !== journal.steps.find((step) => step.directory === rootNode.directory)!.treeDigest) throw new Error("Root source changed before workbench graph refresh"); - const compiled = await compileWorkspaceRepository({rootDirectory: await location(plan.workbench, rootNode.directory), resolveResource}); - // Managed repositories need not currently be reachable from the workspace. - // Keep them discoverable/checkpointed until explicitly removed by the user. - const resources = new Map(compiled.resources.map((node) => [node.key, node])); - for (const node of plan.nodes.filter((entry) => entry.kind !== "workspace")) { - const step = journal.steps.find((entry) => entry.directory === node.directory)!; - const source = {resolver: "git" as const, repository: node.source.repository, commit: step.commit!}; - const key = `${node.kind}\0${source.repository}\0${source.commit}`; - if (resources.has(key)) continue; - const directory = await location(plan.workbench, node.directory); - const standalone = await compileCapabilityResourceRepository({rootDirectory: directory, kind: node.kind as "package" | "interface", source, resolveResource}); - for (const dependency of standalone.resources) resources.set(dependency.key, dependency); - resources.set(key, {key, kind: node.kind as "package" | "interface", source, directory, lock: standalone.lock, resource: standalone.resource, dependencies: standalone.directResources}); - } - await writeJournal(graphFile, {formatVersion: 1, quixos: compiled.lock.quixos, - directResources: [...compiled.directResources.entries()].map(([bindingKey, node]) => {const [kind, binding] = bindingKey.split("\0"); return {kind, binding, resourceKey: node.key, directory: node.directory};}), - resources: [...resources.values()].map((node) => ({key: node.key, kind: node.kind, source: node.source, directory: node.directory, - resourceId: node.resource.kind === "interface" ? node.resource.revision.interfaceId : node.resource.revision.packageId, - revisionId: node.resource.revision.revisionId, dependencies: [...node.dependencies.entries()].map(([binding, dependency]) => ({binding, resourceKey: dependency.key}))})), + return { + id, + journal: filename, + revisions: journal.steps.map((step) => ({ directory: step.directory, commit: step.commit })), + activated: false, + }; + } catch (error) { + throw new Error(`${error instanceof Error ? error.message : String(error)}; upgrade journal ${filename}`, { + cause: error, }); } - return {id, journal: filename, revisions: journal.steps.map((step) => ({directory: step.directory, commit: step.commit})), activated: false}; - } catch (error) {throw new Error(`${error instanceof Error ? error.message : String(error)}; upgrade journal ${filename}`, {cause: error});} }); }; diff --git a/src/capability-language/resource-cli.ts b/src/capability-language/resource-cli.ts index 7ae8179..77c8de3 100644 --- a/src/capability-language/resource-cli.ts +++ b/src/capability-language/resource-cli.ts @@ -3,10 +3,7 @@ import { writeFile } from "node:fs/promises"; import process from "node:process"; import { bindingSchema } from "../bindings/index.js"; -import { - compileCapabilityResourceRepository, - type ResolvedCapabilityResource, -} from "./assembly.js"; +import { compileCapabilityResourceRepository, type ResolvedCapabilityResource } from "./assembly.js"; import { createGitCapabilityResolver } from "./git-resolver.js"; const usage = `usage: quixos-resource-compile --root DIRECTORY --kind interface|package @@ -22,7 +19,21 @@ const parseArgs = (args: string[]) => { const key = args[index]; const value = args[index + 1]; if (!key?.startsWith("--") || !value) throw new Error(usage); - if (!["--root", "--kind", "--repository", "--commit", "--checkout-root", "--snapshot-map", "--snapshot-only", "--graph-out", "--schema-out"].includes(key) || values.has(key)) throw new Error(usage); + if ( + ![ + "--root", + "--kind", + "--repository", + "--commit", + "--checkout-root", + "--snapshot-map", + "--snapshot-only", + "--graph-out", + "--schema-out", + ].includes(key) || + values.has(key) + ) + throw new Error(usage); values.set(key, value); } const rootDirectory = values.get("--root"); @@ -30,14 +41,14 @@ const parseArgs = (args: string[]) => { const repository = values.get("--repository"); const commit = values.get("--commit")?.toLowerCase(); const checkoutRoot = values.get("--checkout-root"); - if (!rootDirectory || !repository || !commit || !checkoutRoot || - (kind !== "interface" && kind !== "package")) { + if (!rootDirectory || !repository || !commit || !checkoutRoot || (kind !== "interface" && kind !== "package")) { throw new Error(usage); } if (!/^([0-9a-f]{40}|[0-9a-f]{64})$/.test(commit)) { throw new Error("--commit must be a full Git object ID"); } - if (values.has("--snapshot-only") && values.get("--snapshot-only") !== "true") throw new Error("--snapshot-only accepts true"); + if (values.has("--snapshot-only") && values.get("--snapshot-only") !== "true") + throw new Error("--snapshot-only accepts true"); return { rootDirectory, kind, @@ -56,9 +67,8 @@ const graphEntry = (node: ResolvedCapabilityResource) => ({ kind: node.kind, source: node.source, directory: node.directory, - resourceId: node.resource.kind === "interface" - ? node.resource.revision.interfaceId - : node.resource.revision.packageId, + resourceId: + node.resource.kind === "interface" ? node.resource.revision.interfaceId : node.resource.revision.packageId, revisionId: node.resource.revision.revisionId, dependencies: [...node.dependencies.entries()].map(([binding, dependency]) => ({ binding, @@ -88,21 +98,32 @@ const main = async () => { resolveResource, }); if (options.graphOut) { - await writeFile(options.graphOut, `${JSON.stringify({ - formatVersion: 1, - quixos: compiled.lock.quixos, - root: graphEntry(compiled.resources.find((node) => - node.source.repository === options.repository && - node.source.commit === options.commit && - node.kind === options.kind)!), - resources: compiled.resources.map(graphEntry), - }, null, 2)}\n`); + await writeFile( + options.graphOut, + `${JSON.stringify( + { + formatVersion: 1, + quixos: compiled.lock.quixos, + root: graphEntry( + compiled.resources.find( + (node) => + node.source.repository === options.repository && + node.source.commit === options.commit && + node.kind === options.kind, + )!, + ), + resources: compiled.resources.map(graphEntry), + }, + null, + 2, + )}\n`, + ); } if (options.schemaOut) await writeFile(options.schemaOut, `${JSON.stringify(bindingSchema(compiled), null, 2)}\n`); process.stdout.write(`${JSON.stringify(compiled.resource, null, 2)}\n`); }; main().catch((error: unknown) => { - process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}\n`); + process.stderr.write(`${error instanceof Error ? (error.stack ?? error.message) : String(error)}\n`); process.exitCode = 1; }); diff --git a/src/capability-language/scaffold-recipes.ts b/src/capability-language/scaffold-recipes.ts index bbd0f2c..7a3dd20 100644 --- a/src/capability-language/scaffold-recipes.ts +++ b/src/capability-language/scaffold-recipes.ts @@ -1,33 +1,57 @@ import fs from "node:fs/promises"; import path from "node:path"; -import {contentDigest} from "../capability-model/evolution.js"; -import {validateMigrationCatalog, type MigrationCatalog, type MigrationDeclaration} from "../capability-model/migrations.js"; -import {formatQuixosLock, type GitSource} from "../resource-lock/index.js"; -import {parseQx, walkSyntax} from "./source.js"; -import type {StructuralRequest} from "./structural-plan.js"; -import {addImplementation} from "./implementation-edit.js"; -import {reactPlatformTypes} from "../bindings/react-platform.js"; +import { contentDigest } from "../capability-model/evolution.js"; +import { + validateMigrationCatalog, + type MigrationCatalog, + type MigrationDeclaration, +} from "../capability-model/migrations.js"; +import { formatQuixosLock, type GitSource } from "../resource-lock/index.js"; +import { parseQx, walkSyntax } from "./source.js"; +import type { StructuralRequest } from "./structural-plan.js"; +import { addImplementation } from "./implementation-edit.js"; +import { reactPlatformTypes } from "../bindings/react-platform.js"; -type Source = {repository: string; commit: string}; -type PackageEditModel = {generatedBy: "qx-scaffold-v1"; name: string; id: string; revision: string; exports: {name: string; id: string; file: string; migration?: boolean}[]}; +type Source = { repository: string; commit: string }; +type PackageEditModel = { + generatedBy: "qx-scaffold-v1"; + name: string; + id: string; + revision: string; + exports: { name: string; id: string; file: string; migration?: boolean }[]; +}; export type ScaffoldRecipe = { template?: "typescript" | "typescript-react"; - source: Source; directory?: string; name?: string; id?: string; revision?: string; + source: Source; + directory?: string; + name?: string; + id?: string; + revision?: string; declaration?: string; /** Initial authored files for a composed recipe; only used for a new package. */ initialFiles?: Record; - tools?: {quixos: Source; protocol: Source; helpers: Source; sdk: Source}; + tools?: { quixos: Source; protocol: Source; helpers: Source; sdk: Source }; nixifyPluginUrl?: string; - migration?: Omit & {contracts: Record}; + migration?: Omit & { contracts: Record }; }; const json = (value: unknown) => `${JSON.stringify(value, null, 2)}\n`; const source = (value: Source): GitSource => { - if (!value || typeof value.repository !== "string" || typeof value.commit !== "string") throw new Error("Scaffold requires an exact source identity; use qx-workspace from a registered repository"); + if (!value || typeof value.repository !== "string" || typeof value.commit !== "string") + throw new Error("Scaffold requires an exact source identity; use qx-workspace from a registered repository"); const url = new URL(value.repository); - if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash || !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(value.commit)) throw new Error("Scaffolds require credential-free HTTPS sources and full exact commits"); - return {resolver: "git", ...value}; + if ( + url.protocol !== "https:" || + url.username || + url.password || + url.search || + url.hash || + !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(value.commit) + ) + throw new Error("Scaffolds require credential-free HTTPS sources and full exact commits"); + return { resolver: "git", ...value }; }; -const nixSource = (value: Source) => `git+${source(value).repository}?ref=refs/tags/quixos-reachability/${value.commit}&rev=${value.commit}`; +const nixSource = (value: Source) => + `git+${source(value).repository}?ref=refs/tags/quixos-reachability/${value.commit}&rev=${value.commit}`; const nixString = (value: string) => JSON.stringify(value).replaceAll("${", "\\${"); const safeName = (name: string | undefined): string => { if (!name || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) throw new Error("Scaffold requires a simple authored name"); @@ -35,7 +59,8 @@ const safeName = (name: string | undefined): string => { }; const ownedJson = async (root: string, file: string): Promise => { const target = path.join(root, file); - if (!(await fs.realpath(target)).startsWith(`${await fs.realpath(root)}/`)) throw new Error("Scaffold input escapes repository"); + if (!(await fs.realpath(target)).startsWith(`${await fs.realpath(root)}/`)) + throw new Error("Scaffold input escapes repository"); const value = JSON.parse(await fs.readFile(target, "utf8")); if (value.generatedBy !== "qx-scaffold-v1") throw new Error(`Not scaffold-owned: ${file}`); return value; @@ -43,45 +68,133 @@ const ownedJson = async (root: string, file: string): Promise => { /** Recipes describe structural edits; planStructure owns validation/journaling. * Implementation files are created once; later edits preserve authored wiring. */ -export const scaffoldRecipe = async (root: string, command: "package" | "function" | "migration" | "refresh", spec: ScaffoldRecipe): Promise => { - if (command === "refresh") throw new Error("Scaffold refresh has been removed. Edit declarations and typed server wiring directly, then run qx-workspace check. Use scaffold function to add a declaration and handler together."); +export const scaffoldRecipe = async ( + root: string, + command: "package" | "function" | "migration" | "refresh", + spec: ScaffoldRecipe, +): Promise => { + if (command === "refresh") + throw new Error( + "Scaffold refresh has been removed. Edit declarations and typed server wiring directly, then run qx-workspace check. Use scaffold function to add a declaration and handler together.", + ); source(spec.source); - if (spec.directory && !/^[A-Za-z0-9_-][A-Za-z0-9_.-]*(?:\/[A-Za-z0-9_-][A-Za-z0-9_.-]*)*$/.test(spec.directory)) throw new Error("Scaffold directory must be contained"); + if (spec.directory && !/^[A-Za-z0-9_-][A-Za-z0-9_.-]*(?:\/[A-Za-z0-9_-][A-Za-z0-9_.-]*)*$/.test(spec.directory)) + throw new Error("Scaffold directory must be contained"); const prefix = spec.directory ? `${spec.directory}/` : ""; const files: StructuralRequest["files"] = []; - const create = (file: string, content: string) => files.push({file: prefix + file, create: content}); - const generated = (file: string, content: string) => files.push({file: prefix + file, generated: content}); + const create = (file: string, content: string) => files.push({ file: prefix + file, create: content }); + const generated = (file: string, content: string) => files.push({ file: prefix + file, generated: content }); let packageModel: PackageEditModel; - let catalog: MigrationCatalog & {generatedBy: "qx-scaffold-v1"}; + let catalog: MigrationCatalog & { generatedBy: "qx-scaffold-v1" }; if (command === "package") { const name = safeName(spec.name); - if (spec.template && !["typescript", "typescript-react"].includes(spec.template)) throw new Error("Unknown package template"); + if (spec.template && !["typescript", "typescript-react"].includes(spec.template)) + throw new Error("Unknown package template"); const react = spec.template === "typescript-react"; - if (!spec.id || !spec.revision || !spec.tools) throw new Error("Package scaffold requires id, revision, and exact quixos/protocol/helpers/sdk tool sources"); + if (!spec.id || !spec.revision || !spec.tools) + throw new Error("Package scaffold requires id, revision, and exact quixos/protocol/helpers/sdk tool sources"); Object.values(spec.tools).forEach(source); - packageModel = {generatedBy: "qx-scaffold-v1", name, id: spec.id, revision: spec.revision, exports: []}; - catalog = {generatedBy: "qx-scaffold-v1", schemaVersion: 1, contracts: {}, migrations: []}; - if (react) packageModel.exports.push({name: "sourceGet", id: `export:${name}:source`, file: "src/impl/sourceGet.ts"}); - create("package.qx", `package ${name} id ${JSON.stringify(spec.id)} revision ${JSON.stringify(spec.revision)} {\n${react ? ` function sourceGet id ${JSON.stringify(`export:${name}:source`)} : unit -> string;\n` : ""}}\n`); - create("quixos.lock", formatQuixosLock({formatVersion: 1, quixos: source(spec.tools.quixos), resources: []})); - create("package.json", json({name: `@quixos/${name.toLowerCase()}`, version: "0.1.0", private: true, type: "module", packageManager: "yarn@4.18.0", - scripts: {build: "tsc -p tsconfig.json", typecheck: "tsc --noEmit"}, dependencies: {"@quixos/camino-package-runtime": `${spec.tools.sdk.repository}#commit=${spec.tools.sdk.commit}`}, - devDependencies: {"@types/node": "^24", typescript: "^7.0.2", ...(react ? {react: "^18.3.1", "@types/react": "^18.3.12", esbuild: "^0.25.12"} : {})}})); - create("tsconfig.json", json({compilerOptions: {target: "ES2023", module: "NodeNext", moduleResolution: "NodeNext", strict: true, types: ["node", ...(react ? ["react"] : [])], outDir: "dist", rootDir: "src", skipLibCheck: true, ...(react ? {jsx: "react-jsx", esModuleInterop: true} : {})}, include: ["src/**/*.ts", "src/**/*.tsx"]})); + packageModel = { generatedBy: "qx-scaffold-v1", name, id: spec.id, revision: spec.revision, exports: [] }; + catalog = { generatedBy: "qx-scaffold-v1", schemaVersion: 1, contracts: {}, migrations: [] }; + if (react) + packageModel.exports.push({ name: "sourceGet", id: `export:${name}:source`, file: "src/impl/sourceGet.ts" }); + create( + "package.qx", + `package ${name} id ${JSON.stringify(spec.id)} revision ${JSON.stringify(spec.revision)} {\n${react ? ` function sourceGet id ${JSON.stringify(`export:${name}:source`)} : unit -> string;\n` : ""}}\n`, + ); + create("quixos.lock", formatQuixosLock({ formatVersion: 1, quixos: source(spec.tools.quixos), resources: [] })); + create( + "package.json", + json({ + name: `@quixos/${name.toLowerCase()}`, + version: "0.1.0", + private: true, + type: "module", + packageManager: "yarn@4.18.0", + scripts: { build: "tsc -p tsconfig.json", typecheck: "tsc --noEmit" }, + dependencies: { + "@quixos/camino-package-runtime": `${spec.tools.sdk.repository}#commit=${spec.tools.sdk.commit}`, + }, + devDependencies: { + "@types/node": "^24", + typescript: "^7.0.2", + ...(react ? { react: "^18.3.1", "@types/react": "^18.3.12", esbuild: "^0.25.12" } : {}), + }, + }), + ); + create( + "tsconfig.json", + json({ + compilerOptions: { + target: "ES2023", + module: "NodeNext", + moduleResolution: "NodeNext", + strict: true, + types: ["node", ...(react ? ["react"] : [])], + outDir: "dist", + rootDir: "src", + skipLibCheck: true, + ...(react ? { jsx: "react-jsx", esModuleInterop: true } : {}), + }, + include: ["src/**/*.ts", "src/**/*.tsx"], + }), + ); if (react) { - create("src/component.tsx", `// Props are opaque at the platform boundary until capability generics exist.\nexport default function Component(_props: {camino: unknown; render: unknown; dispatch: (action: unknown) => void}) {\n return

${name}

Edit this component, then run qx-workspace check.

;\n}\n`); - create("src/impl/sourceGet.ts", `import componentSource from "../component.js?browser-source";\nimport type {Implementation} from "../gen/qx.js";\nexport const handler: Implementation["sourceGet"] = () => componentSource;\n`); - create("src/browser-assets.d.ts", `declare module "*?browser-source" { const source: string; export default source; }\ndeclare module "*.css" {}\n`); + create( + "src/component.tsx", + `// Props are opaque at the platform boundary until capability generics exist.\nexport default function Component(_props: {camino: unknown; render: unknown; dispatch: (action: unknown) => void}) {\n return

${name}

Edit this component, then run qx-workspace check.

;\n}\n`, + ); + create( + "src/impl/sourceGet.ts", + `import componentSource from "../component.js?browser-source";\nimport type {Implementation} from "../gen/qx.js";\nexport const handler: Implementation["sourceGet"] = () => componentSource;\n`, + ); + create( + "src/browser-assets.d.ts", + `declare module "*?browser-source" { const source: string; export default source; }\ndeclare module "*.css" {}\n`, + ); create("src/gen/web-studio-react-runtime.d.ts", reactPlatformTypes); } create(".gitignore", "node_modules/\ndist/\n.quixos/\nresult\n.yarn/install-state.gz\n"); - create(".yarnrc.yml", `nodeLinker: node-modules\nenableScripts: true\nnpmMinimalAgeGate: 0\napprovedGitRepositories:\n - ${JSON.stringify(spec.tools.sdk.repository)}\nsupportedArchitectures:\n os: [current, linux]\n cpu: [current, x64, arm64]\n libc: [current, glibc]\n`); - const nixifyPluginUrl = spec.nixifyPluginUrl ?? "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/yarn-plugin-nixify-patched/raw/commit/4528fdd20b30d869262443b3f044549810e75fb8/dist/yarn-plugin-nixify.js"; + create( + ".yarnrc.yml", + `nodeLinker: node-modules\nenableScripts: true\nnpmMinimalAgeGate: 0\napprovedGitRepositories:\n - ${JSON.stringify(spec.tools.sdk.repository)}\nsupportedArchitectures:\n os: [current, linux]\n cpu: [current, x64, arm64]\n libc: [current, glibc]\n`, + ); + const nixifyPluginUrl = + spec.nixifyPluginUrl ?? + "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/yarn-plugin-nixify-patched/raw/commit/4528fdd20b30d869262443b3f044549810e75fb8/dist/yarn-plugin-nixify.js"; const plugin = new URL(nixifyPluginUrl); - if (plugin.protocol !== "https:" || plugin.username || plugin.password || plugin.search || plugin.hash || !/\/commit\/[a-f0-9]{40,64}\//.test(plugin.pathname)) throw new Error("Nixify plugin must have an exact credential-free HTTPS commit URL"); - generated("quixos.toolchain.json", json({generatedBy: "qx-scaffold-v1", nixifyPluginUrl})); - create("quixos.check.json", json({backend: "typescript", bindingOutput: "src/gen/qx.ts", ...(react ? {options: {messages: {"org.quixos.web-studio.ReactProps": {module: "@quixos/camino-package-runtime", export: "opaqueReactPropsBinding"}}}} : {})})); - create("flake.nix", `{ + if ( + plugin.protocol !== "https:" || + plugin.username || + plugin.password || + plugin.search || + plugin.hash || + !/\/commit\/[a-f0-9]{40,64}\//.test(plugin.pathname) + ) + throw new Error("Nixify plugin must have an exact credential-free HTTPS commit URL"); + generated("quixos.toolchain.json", json({ generatedBy: "qx-scaffold-v1", nixifyPluginUrl })); + create( + "quixos.check.json", + json({ + backend: "typescript", + bindingOutput: "src/gen/qx.ts", + ...(react + ? { + options: { + messages: { + "org.quixos.web-studio.ReactProps": { + module: "@quixos/camino-package-runtime", + export: "opaqueReactPropsBinding", + }, + }, + }, + } + : {}), + }), + ); + create( + "flake.nix", + `{ inputs.protocol.url = ${nixString(nixSource(spec.tools.protocol))}; inputs.nixpkgs.follows = "protocol/nixpkgs"; inputs.flake-utils.follows = "protocol/flake-utils"; @@ -93,54 +206,101 @@ export const scaffoldRecipe = async (root: string, command: "package" | "functio migrationEntrypoint = "dist/migrate.js"; installServer = { libexecName = ${JSON.stringify(name.toLowerCase())}; descriptorPath = "descriptor.quixos-package.txtpb"; }; }; -}\n`); +}\n`, + ); } else { catalog = await ownedJson(root, prefix + "quixos.migrations.json"); // Derive the edit model from authored declarations; it is never persisted. const authored = await fs.readFile(path.join(root, prefix, "package.qx"), "utf8"); const syntax = parseQx(authored); - if (syntax.diagnostics.length) throw new Error("Cannot scaffold into an invalid package.qx; fix the reported syntax first"); - const declaration = [...walkSyntax(syntax.root)].find(node => node.kind === "packageResourceDecl"); + if (syntax.diagnostics.length) + throw new Error("Cannot scaffold into an invalid package.qx; fix the reported syntax first"); + const declaration = [...walkSyntax(syntax.root)].find((node) => node.kind === "packageResourceDecl"); if (!declaration) throw new Error("Expected a package declaration"); const text = (node: typeof declaration) => authored.slice(node.start, node.end); - const literals = declaration.children.filter(node => node.kind === "stringLiteral"); - packageModel = {generatedBy: "qx-scaffold-v1", name: text(declaration.children.find(node => node.kind === "identifier")!), - id: JSON.parse(text(literals[0])), revision: JSON.parse(text(literals[1])), exports: []}; + const literals = declaration.children.filter((node) => node.kind === "stringLiteral"); + packageModel = { + generatedBy: "qx-scaffold-v1", + name: text(declaration.children.find((node) => node.kind === "identifier")!), + id: JSON.parse(text(literals[0])), + revision: JSON.parse(text(literals[1])), + exports: [], + }; for (const node of walkSyntax(declaration)) { - if (!["packageFunctionExport", "packageOperationExport", "packageConstructorExport"].includes(node.kind)) continue; - const name = safeName(text(node.children.find(child => child.kind === "identifier")!)); - const id = JSON.parse(text(node.children.find(child => child.kind === "stringLiteral")!)); - if (packageModel.exports.some(entry => entry.id === id || entry.name === name)) throw new Error("Duplicate package export name or ID"); - const migration = catalog.migrations.find(entry => entry.implementation.exportId === id); - packageModel.exports.push({name, id, file: migration?.implementation.file ?? `src/impl/${name}.ts`, ...(migration ? {migration: true} : {})}); + if (!["packageFunctionExport", "packageOperationExport", "packageConstructorExport"].includes(node.kind)) + continue; + const name = safeName(text(node.children.find((child) => child.kind === "identifier")!)); + const id = JSON.parse(text(node.children.find((child) => child.kind === "stringLiteral")!)); + if (packageModel.exports.some((entry) => entry.id === id || entry.name === name)) + throw new Error("Duplicate package export name or ID"); + const migration = catalog.migrations.find((entry) => entry.implementation.exportId === id); + packageModel.exports.push({ + name, + id, + file: migration?.implementation.file ?? `src/impl/${name}.ts`, + ...(migration ? { migration: true } : {}), + }); } { const name = safeName(spec.name); - if (!spec.id || packageModel.exports.some((entry) => entry.id === spec.id || entry.name === name)) throw new Error("New export requires a unique name and ID"); + if (!spec.id || packageModel.exports.some((entry) => entry.id === spec.id || entry.name === name)) + throw new Error("New export requires a unique name and ID"); const declaration = spec.declaration ?? `function ${name} id ${JSON.stringify(spec.id)} : unit -> unit;`; const parsed = parseQx(`package Scaffold id "scaffold" revision "scaffold@1" { ${declaration} }`); - const exports = [...walkSyntax(parsed.root)].filter((node) => ["packageFunctionExport", "packageOperationExport", "packageConstructorExport"].includes(node.kind)); - if (parsed.diagnostics.length || exports.length !== 1) throw new Error("Expected one valid package export declaration"); + const exports = [...walkSyntax(parsed.root)].filter((node) => + ["packageFunctionExport", "packageOperationExport", "packageConstructorExport"].includes(node.kind), + ); + if (parsed.diagnostics.length || exports.length !== 1) + throw new Error("Expected one valid package export declaration"); const wrapped = `package Scaffold id "scaffold" revision "scaffold@1" { ${declaration} }`; const node = exports[0]; const derived = [...walkSyntax(node)].some((entry) => entry.kind === "eventClause"); - if (command === "migration" && (node.kind !== "packageFunctionExport" || spec.declaration)) throw new Error("Migration exports use the scaffold's unit function declaration and dedicated migration entrypoint"); - if (wrapped.slice(node.children.find((child) => child.kind === "identifier")!.start, node.children.find((child) => child.kind === "identifier")!.end) !== name - || JSON.parse(wrapped.slice(node.children.find((child) => child.kind === "stringLiteral")!.start, node.children.find((child) => child.kind === "stringLiteral")!.end)) !== spec.id) throw new Error("Declaration name/ID must match its registration"); - files.push({file: prefix + "package.qx", edits: [{operation: "append", parent: {kind: "packageResourceDecl", id: packageModel.id}, source: declaration}]}); + if (command === "migration" && (node.kind !== "packageFunctionExport" || spec.declaration)) + throw new Error( + "Migration exports use the scaffold's unit function declaration and dedicated migration entrypoint", + ); + if ( + wrapped.slice( + node.children.find((child) => child.kind === "identifier")!.start, + node.children.find((child) => child.kind === "identifier")!.end, + ) !== name || + JSON.parse( + wrapped.slice( + node.children.find((child) => child.kind === "stringLiteral")!.start, + node.children.find((child) => child.kind === "stringLiteral")!.end, + ), + ) !== spec.id + ) + throw new Error("Declaration name/ID must match its registration"); + files.push({ + file: prefix + "package.qx", + edits: [ + { operation: "append", parent: { kind: "packageResourceDecl", id: packageModel.id }, source: declaration }, + ], + }); const file = `src/${command === "migration" ? "migrations" : "impl"}/${name}.ts`; - const implementation = command === "migration" ? `import type {MigrationContext} from "@quixos/camino-package-runtime";\nexport const handler = async (_context: MigrationContext): Promise => { throw new Error(${JSON.stringify(`Implement migration ${name}`)}); };\n` - : `import type {Implementation} from "../gen/qx.js";\nexport const handler: Implementation[${JSON.stringify(name)}] = ${derived ? '{kind: "derived", get: ' : ""}async (_context) => { throw new Error(${JSON.stringify(`Implement ${name}`)}); }${derived ? "}" : ""};\n`; + const implementation = + command === "migration" + ? `import type {MigrationContext} from "@quixos/camino-package-runtime";\nexport const handler = async (_context: MigrationContext): Promise => { throw new Error(${JSON.stringify(`Implement migration ${name}`)}); };\n` + : `import type {Implementation} from "../gen/qx.js";\nexport const handler: Implementation[${JSON.stringify(name)}] = ${derived ? '{kind: "derived", get: ' : ""}async (_context) => { throw new Error(${JSON.stringify(`Implement ${name}`)}); }${derived ? "}" : ""};\n`; create(file, implementation); - packageModel.exports.push({name, id: spec.id, file, ...(command === "migration" ? {migration: true} : {})}); + packageModel.exports.push({ name, id: spec.id, file, ...(command === "migration" ? { migration: true } : {}) }); if (command === "migration") { - if (!spec.migration) throw new Error("Migration scaffold requires retained contracts and an explicit transition"); - const {contracts, ...transition} = spec.migration; + if (!spec.migration) + throw new Error("Migration scaffold requires retained contracts and an explicit transition"); + const { contracts, ...transition } = spec.migration; for (const [digest, contract] of Object.entries(contracts)) { - if (contentDigest(contract) !== digest || (catalog.contracts[digest] && contentDigest(catalog.contracts[digest]) !== digest)) throw new Error("Retained migration contract mismatch"); + if ( + contentDigest(contract) !== digest || + (catalog.contracts[digest] && contentDigest(catalog.contracts[digest]) !== digest) + ) + throw new Error("Retained migration contract mismatch"); catalog.contracts[digest] = contract; } - catalog.migrations.push({...transition, implementation: {exportId: spec.id, file, digest: contentDigest(implementation)}}); + catalog.migrations.push({ + ...transition, + implementation: { exportId: spec.id, file, digest: contentDigest(implementation) }, + }); } } } @@ -150,44 +310,108 @@ export const scaffoldRecipe = async (root: string, command: "package" | "functio const entry = packageModel.exports.at(-1)!; const file = prefix + "src/server.ts"; const before = await fs.readFile(path.join(root, file), "utf8"); - files.push({file, expected: before, replace: addImplementation(before, "createRuntime", entry.name, `./${entry.file.slice(4, -3)}.js`, !!entry.migration)}); + files.push({ + file, + expected: before, + replace: addImplementation( + before, + "createRuntime", + entry.name, + `./${entry.file.slice(4, -3)}.js`, + !!entry.migration, + ), + }); if (entry.migration) { const file = prefix + "src/migrate.ts"; const before = await fs.readFile(path.join(root, file), "utf8"); - files.push({file, expected: before, replace: addImplementation(before, "serveMigration", entry.id, `./${entry.file.slice(4, -3)}.js`)}); + files.push({ + file, + expected: before, + replace: addImplementation(before, "serveMigration", entry.id, `./${entry.file.slice(4, -3)}.js`), + }); } } else { - create("src/server.ts", `import {servePackageRuntime} from "@quixos/camino-package-runtime";\nimport {createRuntime} from "./gen/qx.js";\n` + packageModel.exports.filter((entry) => !entry.migration).map((entry, index) => `import {handler as impl${index}} from ${JSON.stringify(`./${entry.file.slice(4, -3)}.js`)};\n`).join("") + - `servePackageRuntime(createRuntime({\n` + packageModel.exports.map((entry) => ` ${JSON.stringify(entry.name)}: ${entry.migration ? 'async () => { throw new Error("Migration-only export"); }' : `impl${packageModel.exports.filter((value) => !value.migration).indexOf(entry)}`},`).join("\n") + `\n}));\n`); - const migrations = packageModel.exports.filter((entry) => entry.migration); - create("src/migrate.ts", `import {serveMigration} from "@quixos/camino-package-runtime";\n` + migrations.map((entry, index) => `import {handler as impl${index}} from ${JSON.stringify(`./${entry.file.slice(4, -3)}.js`)};\n`).join("") + `await serveMigration({${migrations.map((entry, index) => `${JSON.stringify(entry.id)}: impl${index}`).join(", ")}});\n`); + create( + "src/server.ts", + `import {servePackageRuntime} from "@quixos/camino-package-runtime";\nimport {createRuntime} from "./gen/qx.js";\n` + + packageModel.exports + .filter((entry) => !entry.migration) + .map( + (entry, index) => + `import {handler as impl${index}} from ${JSON.stringify(`./${entry.file.slice(4, -3)}.js`)};\n`, + ) + .join("") + + `servePackageRuntime(createRuntime({\n` + + packageModel.exports + .map( + (entry) => + ` ${JSON.stringify(entry.name)}: ${entry.migration ? 'async () => { throw new Error("Migration-only export"); }' : `impl${packageModel.exports.filter((value) => !value.migration).indexOf(entry)}`},`, + ) + .join("\n") + + `\n}));\n`, + ); + const migrations = packageModel.exports.filter((entry) => entry.migration); + create( + "src/migrate.ts", + `import {serveMigration} from "@quixos/camino-package-runtime";\n` + + migrations + .map( + (entry, index) => + `import {handler as impl${index}} from ${JSON.stringify(`./${entry.file.slice(4, -3)}.js`)};\n`, + ) + .join("") + + `await serveMigration({${migrations.map((entry, index) => `${JSON.stringify(entry.id)}: impl${index}`).join(", ")}});\n`, + ); } - generated("descriptor.quixos-package.txtpb", `# Generated by qx-scaffold-v1\npackage_id: ${JSON.stringify(packageModel.id)}\npackage_revision_id: ${JSON.stringify(packageModel.revision)}\nruntime_protocol_version: "quixos-capabilities-v1"\n` + packageModel.exports.map((entry) => `exports: { export_id: ${JSON.stringify(entry.id)} runtime_symbol: ${JSON.stringify(entry.name)} }\n`).join("")); + generated( + "descriptor.quixos-package.txtpb", + `# Generated by qx-scaffold-v1\npackage_id: ${JSON.stringify(packageModel.id)}\npackage_revision_id: ${JSON.stringify(packageModel.revision)}\nruntime_protocol_version: "quixos-capabilities-v1"\n` + + packageModel.exports + .map( + (entry) => + `exports: { export_id: ${JSON.stringify(entry.id)} runtime_symbol: ${JSON.stringify(entry.name)} }\n`, + ) + .join(""), + ); if (spec.initialFiles) { if (command !== "package") throw new Error("initialFiles is only valid when creating a package"); const authored = spec.initialFiles["package.qx"]; if (authored !== undefined) { const parsed = parseQx(authored); - const declaration = [...walkSyntax(parsed.root)].find(n => n.kind === "packageResourceDecl"); - const literals = declaration?.children.filter(n => n.kind === "stringLiteral") ?? []; - const identifier = declaration?.children.find(n => n.kind === "identifier"); - if (parsed.diagnostics.length || literals.length !== 2 || !identifier || authored.slice(identifier.start, identifier.end) !== spec.name || - JSON.parse(authored.slice(literals[0].start, literals[0].end)) !== spec.id || JSON.parse(authored.slice(literals[1].start, literals[1].end)) !== spec.revision) + const declaration = [...walkSyntax(parsed.root)].find((n) => n.kind === "packageResourceDecl"); + const literals = declaration?.children.filter((n) => n.kind === "stringLiteral") ?? []; + const identifier = declaration?.children.find((n) => n.kind === "identifier"); + if ( + parsed.diagnostics.length || + literals.length !== 2 || + !identifier || + authored.slice(identifier.start, identifier.end) !== spec.name || + JSON.parse(authored.slice(literals[0].start, literals[0].end)) !== spec.id || + JSON.parse(authored.slice(literals[1].start, literals[1].end)) !== spec.revision + ) throw new Error("Initial package declaration must match the provisioned name, ID and revision"); } for (const [file, content] of Object.entries(spec.initialFiles)) { - if (typeof content !== "string" || ["quixos.lock", "flake.nix", "quixos.toolchain.json", "quixos.check.json", "package.json"].includes(file)) throw new Error(`Not an initial authored file: ${file}`); - const existing = files.findIndex(entry => entry.file === prefix + file); + if ( + typeof content !== "string" || + ["quixos.lock", "flake.nix", "quixos.toolchain.json", "quixos.check.json", "package.json"].includes(file) + ) + throw new Error(`Not an initial authored file: ${file}`); + const existing = files.findIndex((entry) => entry.file === prefix + file); if (existing >= 0) files.splice(existing, 1); create(file, content); } // A composed React recipe supplies its own modules and complete server. if (reactRecipe(spec)) { for (const file of ["src/component.tsx", "src/impl/sourceGet.ts", "descriptor.quixos-package.txtpb"]) - if (!(file in spec.initialFiles)) {const index = files.findIndex(entry => entry.file === prefix + file); if (index >= 0) files.splice(index, 1);} + if (!(file in spec.initialFiles)) { + const index = files.findIndex((entry) => entry.file === prefix + file); + if (index >= 0) files.splice(index, 1); + } } } - return {kind: "package", source: spec.source, resourceRoot: spec.directory, validation: "syntax", files}; + return { kind: "package", source: spec.source, resourceRoot: spec.directory, validation: "syntax", files }; }; -const reactRecipe = (spec: ScaffoldRecipe) => spec.template === "typescript-react" && spec.initialFiles?.["package.qx"] && spec.initialFiles?.["src/server.ts"]; +const reactRecipe = (spec: ScaffoldRecipe) => + spec.template === "typescript-react" && spec.initialFiles?.["package.qx"] && spec.initialFiles?.["src/server.ts"]; diff --git a/src/capability-language/scaffold.ts b/src/capability-language/scaffold.ts index c9b4563..d5ef289 100644 --- a/src/capability-language/scaffold.ts +++ b/src/capability-language/scaffold.ts @@ -19,7 +19,11 @@ export const planAtomScaffold = (workspace: string, name: string, id: string) => /** Validate an in-memory proposal before creating files. Never replace a fragment. */ export const scaffoldAtom = async (options: { - root: string; name: string; id: string; write: boolean; resolveResource: CapabilityRepositoryResolver; + root: string; + name: string; + id: string; + write: boolean; + resolveResource: CapabilityRepositoryResolver; }) => { const before = await readQxSource(options.root, "workspace.qx"); const plan = planAtomScaffold(before, options.name, options.id); @@ -31,14 +35,17 @@ export const scaffoldAtom = async (options: { if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; } const observed = new Map(); - await compileWorkspaceRepository({ rootDirectory: options.root, resolveResource: options.resolveResource, + await compileWorkspaceRepository({ + rootDirectory: options.root, + resolveResource: options.resolveResource, readSource: async (name) => { if (name === "workspace.qx") return plan.workspace; if (name === plan.fileName) return plan.fragment; const text = await readQxSource(options.root, name); observed.set(name, text); return text; - } }); + }, + }); if (!options.write) return plan; const workspacePath = path.join(options.root, "workspace.qx"); const temporary = path.join(options.root, `.qx-scaffold-${randomUUID()}.tmp`); @@ -47,13 +54,22 @@ export const scaffoldAtom = async (options: { try { const fragment = await open(fragmentPath, "wx"); createdFragment = true; - try { await fragment.writeFile(plan.fragment); } finally { await fragment.close(); } + try { + await fragment.writeFile(plan.fragment); + } finally { + await fragment.close(); + } const file = await open(temporary, "wx", (await lstat(workspacePath)).mode); createdTemporary = true; - try { await file.writeFile(plan.workspace); } finally { await file.close(); } + try { + await file.writeFile(plan.workspace); + } finally { + await file.close(); + } observed.set("workspace.qx", before); for (const [name, text] of observed) { - if (await readQxSource(options.root, name) !== text) throw new Error(`QX source changed during scaffolding: ${name}`); + if ((await readQxSource(options.root, name)) !== text) + throw new Error(`QX source changed during scaffolding: ${name}`); } await rename(temporary, workspacePath); createdTemporary = false; diff --git a/src/capability-language/source-loader.ts b/src/capability-language/source-loader.ts index f8e981c..7ca00b1 100644 --- a/src/capability-language/source-loader.ts +++ b/src/capability-language/source-loader.ts @@ -16,9 +16,7 @@ export const readQxSource = async (root: string, name: string) => { }; /** Local imports share one workspace scope; paths are always repository-relative. */ -export const resolveQxSources = async ( - read: (name: string) => Promise, entry = "workspace.qx", -) => { +export const resolveQxSources = async (read: (name: string) => Promise, entry = "workspace.qx") => { const files = new Map(); const active: string[] = []; const visited = new Set(); @@ -34,8 +32,8 @@ export const resolveQxSources = async ( if (visited.has(name)) return; const text = await read(name); const syntax = parseQx(text, name); - if (syntax.diagnostics.length) throw new Error(syntax.diagnostics.map((d) => - `${name}:${d.line}:${d.column + 1}: ${d.message}`).join("\n")); + if (syntax.diagnostics.length) + throw new Error(syntax.diagnostics.map((d) => `${name}:${d.line}:${d.column + 1}: ${d.message}`).join("\n")); const declaration = syntax.root.children[0]!; if (declaration.kind !== (root ? "workspaceDecl" : "fragmentDecl")) throw new Error(`${name}: expected ${root ? "workspace" : "fragment"} document`); @@ -60,16 +58,22 @@ export const resolveQxSources = async ( }; await visit(entry, true); return { - source, sourceFiles: [...files.keys()], files, + source, + sourceFiles: [...files.keys()], + files, originalPosition(line: number, column: number) { const lines = source.split("\n"); - const offset = lines.slice(0, line - 1).reduce((sum, value) => sum + value.length + 1, 0) + + const offset = + lines.slice(0, line - 1).reduce((sum, value) => sum + value.length + 1, 0) + [...(lines[line - 1] ?? "")].slice(0, column).join("").length; const segment = segments.find((entry) => entry.start <= offset && entry.end > offset); if (!segment) return { fileName: entry, line, column }; const prefix = files.get(segment.fileName)!.slice(0, segment.sourceStart + offset - segment.start); - return { fileName: segment.fileName, line: prefix.split("\n").length, - column: prefix.length - prefix.lastIndexOf("\n") - 1 }; + return { + fileName: segment.fileName, + line: prefix.split("\n").length, + column: prefix.length - prefix.lastIndexOf("\n") - 1, + }; }, }; }; diff --git a/src/capability-language/source.ts b/src/capability-language/source.ts index 3f6a2a7..82e7e80 100644 --- a/src/capability-language/source.ts +++ b/src/capability-language/source.ts @@ -17,20 +17,26 @@ export const parseQx = (source: string, fileName = "") => { kind: QuixosCapabilityParser.ruleNames[context.ruleIndex]!, start: offset(context.start?.start ?? 0), end: offset((context.stop?.stop ?? -1) + 1), - children: context.children.flatMap((child) => child instanceof ParserRuleContext ? [node(child)] : []), + children: context.children.flatMap((child) => (child instanceof ParserRuleContext ? [node(child)] : [])), }); return { - source, fileName, + source, + fileName, root: node(parsed.tree), diagnostics: parsed.diagnostics.map((diagnostic) => { const line = source.split("\n")[diagnostic.line - 1] ?? ""; return { ...diagnostic, column: [...line].slice(0, diagnostic.column).join("").length }; }), - tokens: parsed.tokens.getTokens().filter((token) => token.type !== -1).map((token) => ({ - kind: QuixosCapabilityParser.symbolicNames[token.type] ?? "token", - start: offset(token.start), end: offset(token.stop + 1), - text: token.text ?? "", trivia: token.channel !== 0, - })), + tokens: parsed.tokens + .getTokens() + .filter((token) => token.type !== -1) + .map((token) => ({ + kind: QuixosCapabilityParser.symbolicNames[token.type] ?? "token", + start: offset(token.start), + end: offset(token.stop + 1), + text: token.text ?? "", + trivia: token.channel !== 0, + })), }; }; @@ -41,8 +47,14 @@ export const applySourceEdits = (source: string, edits: readonly SourceEdit[]) = let previousStart = -1; let result = ""; for (const edit of sorted) { - if (!Number.isInteger(edit.start) || !Number.isInteger(edit.end) || - edit.start < end || edit.start === previousStart || edit.end < edit.start || edit.end > source.length) { + if ( + !Number.isInteger(edit.start) || + !Number.isInteger(edit.end) || + edit.start < end || + edit.start === previousStart || + edit.end < edit.start || + edit.end > source.length + ) { throw new Error("Invalid or overlapping source edits"); } result += source.slice(end, edit.start) + edit.text; @@ -68,14 +80,27 @@ export const lintQx = (source: string, fileName = "") => { const importPath: string = JSON.parse(source.slice(literal.start, literal.end)); let message: string | undefined; let code = "invalid-source-import"; - try { validateQxImportPath(importPath); } catch (error) { message = (error as Error).message; } - if (!message && seen.has(importPath)) { code = "duplicate-source-import"; message = `Repeated local import ${importPath}`; } + try { + validateQxImportPath(importPath); + } catch (error) { + message = (error as Error).message; + } + if (!message && seen.has(importPath)) { + code = "duplicate-source-import"; + message = `Repeated local import ${importPath}`; + } seen.add(importPath); if (message) { const prefix = source.slice(0, node.start); - diagnostics.push({ phase: "syntax", code, message, fileName, - line: prefix.split("\n").length, column: prefix.length - prefix.lastIndexOf("\n") - 1, - severity: code === "duplicate-source-import" ? "warning" : "error" }); + diagnostics.push({ + phase: "syntax", + code, + message, + fileName, + line: prefix.split("\n").length, + column: prefix.length - prefix.lastIndexOf("\n") - 1, + severity: code === "duplicate-source-import" ? "warning" : "error", + }); } } return diagnostics; @@ -117,12 +142,15 @@ export const addWorkspaceImport = (source: string, importPath: string) => { } const brace = syntax.tokens.find((token) => token.kind === "LBRACE")!; const newline = source.includes("\r\n") ? "\r\n" : "\n"; - return applySourceEdits(source, [{ start: brace.end, end: brace.end, - text: `${newline} import ${JSON.stringify(importPath)};` }]); + return applySourceEdits(source, [ + { start: brace.end, end: brace.end, text: `${newline} import ${JSON.stringify(importPath)};` }, + ]); }; export const validateQxImportPath = (value: string) => { - if (!/^(?:[A-Za-z0-9_-][A-Za-z0-9_.-]*\/)*[A-Za-z0-9_-][A-Za-z0-9_.-]*\.qx$/.test(value) || - value.split("/").some((part) => part === "." || part === "..")) + if ( + !/^(?:[A-Za-z0-9_-][A-Za-z0-9_.-]*\/)*[A-Za-z0-9_-][A-Za-z0-9_.-]*\.qx$/.test(value) || + value.split("/").some((part) => part === "." || part === "..") + ) throw new Error(`Invalid repository-relative QX import path: ${value}`); }; diff --git a/src/capability-language/structural-edits.ts b/src/capability-language/structural-edits.ts index 7ba8ac3..c73b1a7 100644 --- a/src/capability-language/structural-edits.ts +++ b/src/capability-language/structural-edits.ts @@ -12,23 +12,46 @@ export type StructuralEdit = | { operation: "import"; kind: "interface" | "package"; name: string } | { operation: "semantic-major"; target: StructuralSelector; major: number } | { operation: "conformance-id"; target: StructuralSelector; id: string } - | { operation: "quixos-pin"; source: {repository: string; commit: string} } - | { operation: "dependency"; kind: "interface" | "package"; name: string; source: {repository: string; commit: string} | null }; + | { operation: "quixos-pin"; source: { repository: string; commit: string } } + | { + operation: "dependency"; + kind: "interface" | "package"; + name: string; + source: { repository: string; commit: string } | null; + }; // Deliberately exclude valueType/identifier/stringLiteral: callers operate on // declaration structure, not arbitrary token offsets or lockfile text patches. -const selectable = new Set(["workspaceDecl", "fragmentDecl", "interfaceResourceDecl", "packageResourceDecl", "atomDecl", - "valueMember", "relationshipMember", "operationMember", "packageOperationExport", "packageFunctionExport", "packageConstructorExport", - "conformanceDecl", "stateDecl", "edgeDecl", "constructorBindingDecl", "resourceImportDecl", "sourceImportDecl", "operationBindingDecl"]); +const selectable = new Set([ + "workspaceDecl", + "fragmentDecl", + "interfaceResourceDecl", + "packageResourceDecl", + "atomDecl", + "valueMember", + "relationshipMember", + "operationMember", + "packageOperationExport", + "packageFunctionExport", + "packageConstructorExport", + "conformanceDecl", + "stateDecl", + "edgeDecl", + "constructorBindingDecl", + "resourceImportDecl", + "sourceImportDecl", + "operationBindingDecl", +]); /** Bind only the template root identity; schema/atom identities are reusable. * The revision's real identity is derived from its containing commit at compile time. */ export const instantiateWorkspaceIdentity = (source: string, workspaceId: string): string => { - if (!/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(workspaceId)) throw new Error("Workspace identity must be a UUID"); + if (!/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(workspaceId)) + throw new Error("Workspace identity must be a UUID"); const syntax = parseQx(source); if (syntax.diagnostics.length) throw new Error("Cannot instantiate a malformed template workspace"); - const declaration = syntax.root.children.find(node => node.kind === "workspaceDecl"); - const literals = declaration?.children.filter(node => node.kind === "stringLiteral"); + const declaration = syntax.root.children.find((node) => node.kind === "workspaceDecl"); + const literals = declaration?.children.filter((node) => node.kind === "stringLiteral"); if (!literals || literals.length !== 3) throw new Error("Template must contain a workspace declaration"); return applySourceEdits(source, [ { ...literals[0], text: JSON.stringify(workspaceId) }, @@ -36,25 +59,48 @@ export const instantiateWorkspaceIdentity = (source: string, workspaceId: string ]); }; -const select = (source: string, selector: StructuralSelector): {node: SyntaxNode; syntax: ReturnType} => { +const select = ( + source: string, + selector: StructuralSelector, +): { node: SyntaxNode; syntax: ReturnType } => { if (!selectable.has(selector.kind)) throw new Error(`Unsupported structural selector ${selector.kind}`); const syntax = parseQx(source); if (syntax.diagnostics.length) throw new Error("Cannot scaffold syntactically invalid QX"); const matches = [...walkSyntax(syntax.root)].filter((node) => { if (node.kind !== selector.kind) return false; - if (selector.name && !node.children.some((child) => child.kind === "identifier" && source.slice(child.start, child.end) === selector.name)) return false; - if (selector.names && JSON.stringify(node.children.filter((child) => child.kind === "identifier").map((child) => source.slice(child.start, child.end))) !== JSON.stringify(selector.names)) return false; + if ( + selector.name && + !node.children.some( + (child) => child.kind === "identifier" && source.slice(child.start, child.end) === selector.name, + ) + ) + return false; + if ( + selector.names && + JSON.stringify( + node.children + .filter((child) => child.kind === "identifier") + .map((child) => source.slice(child.start, child.end)), + ) !== JSON.stringify(selector.names) + ) + return false; if (selector.id) { // Only an explicit ID field counts, not a coincidentally equal revision, // default value, nested declaration, or comment. - const tokens = syntax.tokens.filter((token) => !token.trivia && token.start >= node.start && token.end <= node.end); - const literal = node.children.find((child) => child.kind === "stringLiteral" && tokens.some((token, index) => token.start === child.start && tokens[index - 1]?.kind === "ID")); + const tokens = syntax.tokens.filter( + (token) => !token.trivia && token.start >= node.start && token.end <= node.end, + ); + const literal = node.children.find( + (child) => + child.kind === "stringLiteral" && + tokens.some((token, index) => token.start === child.start && tokens[index - 1]?.kind === "ID"), + ); if (!literal || JSON.parse(source.slice(literal.start, literal.end)) !== selector.id) return false; } return true; }); if (matches.length !== 1) throw new Error(`Structural selector must resolve exactly once (found ${matches.length})`); - return {node: matches[0], syntax}; + return { node: matches[0], syntax }; }; /** Comment-preserving structural edits; every result is parsed before returning. */ @@ -69,87 +115,148 @@ export const editStructure = (source: string, edit: StructuralEdit): string => { const offsets = [0]; for (const character of source) offsets.push(offsets[offsets.length - 1] + character.length); const result = applySourceEdits(source, [ - {start: offsets[literals[0].start!.start], end: offsets[literals[0].stop!.stop + 1], text: JSON.stringify(edit.source.repository)}, - {start: offsets[literals[literals.length - 1].start!.start], end: offsets[literals[literals.length - 1].stop!.stop + 1], text: JSON.stringify(edit.source.commit)}, + { + start: offsets[literals[0].start!.start], + end: offsets[literals[0].stop!.stop + 1], + text: JSON.stringify(edit.source.repository), + }, + { + start: offsets[literals[literals.length - 1].start!.start], + end: offsets[literals[literals.length - 1].stop!.stop + 1], + text: JSON.stringify(edit.source.commit), + }, ]); const checked = parseQuixosLockDocument(result); - if (!checked.ok) throw new Error(`Invalid Quixos pin: ${checked.diagnostics.map((entry) => entry.message).join("; ")}`); + if (!checked.ok) + throw new Error(`Invalid Quixos pin: ${checked.diagnostics.map((entry) => entry.message).join("; ")}`); return result; } if (edit.operation === "import") { - if (!["interface", "package"].includes(edit.kind) || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(edit.name)) throw new Error("Invalid resource import"); + if (!["interface", "package"].includes(edit.kind) || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(edit.name)) + throw new Error("Invalid resource import"); const syntax = parseQx(source); if (syntax.diagnostics.length) throw new Error("Cannot scaffold invalid QX"); const text = `import ${edit.kind} ${edit.name};`; - if ([...walkSyntax(syntax.root)].some((entry) => entry.kind === "resourceImportDecl" && source.slice(entry.start, entry.end).replace(/\s+/g, " ") === text)) return source; + if ( + [...walkSyntax(syntax.root)].some( + (entry) => + entry.kind === "resourceImportDecl" && source.slice(entry.start, entry.end).replace(/\s+/g, " ") === text, + ) + ) + return source; const root = syntax.root.children[0]; - const position = ["workspaceDecl", "fragmentDecl"].includes(root.kind) ? syntax.tokens.find((token) => token.kind === "LBRACE")!.end : root.start; - const result = applySourceEdits(source, [{start: position, end: position, text: `\n${text}\n`}]); + const position = ["workspaceDecl", "fragmentDecl"].includes(root.kind) + ? syntax.tokens.find((token) => token.kind === "LBRACE")!.end + : root.start; + const result = applySourceEdits(source, [{ start: position, end: position, text: `\n${text}\n` }]); if (parseQx(result).diagnostics.length) throw new Error("Invalid resource import position"); return result; } if (edit.operation === "dependency") { const parsed = parseQuixosLockDocument(source); if (!parsed.ok) throw new Error("Cannot scaffold an invalid lockfile"); - if (!["interface", "package"].includes(edit.kind) || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(edit.name)) throw new Error("Invalid dependency selector"); + if (!["interface", "package"].includes(edit.kind) || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(edit.name)) + throw new Error("Invalid dependency selector"); const parser = new QuixosLockParser(new CommonTokenStream(new QuixosLockLexer(CharStream.fromString(source)))); const tree = parser.document(); const offsets = [0]; for (const character of source) offsets.push(offsets[offsets.length - 1] + character.length); - const entries = tree.resourceEntry().filter((entry) => entry.resourceKind().getText() === edit.kind && entry.identifier().getText() === edit.name); + const entries = tree + .resourceEntry() + .filter((entry) => entry.resourceKind().getText() === edit.kind && entry.identifier().getText() === edit.name); if (entries.length > 1) throw new Error("Ambiguous dependency selector"); const entry = entries[0]; - const replacement = edit.source ? `${edit.kind} ${edit.name} source {\n repository ${JSON.stringify(edit.source.repository)};\n commit ${JSON.stringify(edit.source.commit)};\n}` : ""; + const replacement = edit.source + ? `${edit.kind} ${edit.name} source {\n repository ${JSON.stringify(edit.source.repository)};\n commit ${JSON.stringify(edit.source.commit)};\n}` + : ""; if (!entry && !edit.source) throw new Error("Cannot remove an absent dependency"); const start = entry ? offsets[entry.start!.start] : offsets[tree.RBRACE().symbol.start]; const end = entry ? offsets[entry.stop!.stop + 1] : start; - const result = applySourceEdits(source, [{start, end, text: entry ? replacement : `${replacement}\n`}]); + const result = applySourceEdits(source, [{ start, end, text: entry ? replacement : `${replacement}\n` }]); const checked = parseQuixosLockDocument(result); - if (!checked.ok) throw new Error(`Invalid dependency change: ${checked.diagnostics.map((entry) => entry.message).join("; ")}`); + if (!checked.ok) + throw new Error(`Invalid dependency change: ${checked.diagnostics.map((entry) => entry.message).join("; ")}`); return result; } - const {node, syntax} = select(source, edit.operation === "append" ? edit.parent : edit.target); + const { node, syntax } = select(source, edit.operation === "append" ? edit.parent : edit.target); let result: string; if (edit.operation === "semantic-major") { - if (!["conformanceDecl", "packageResourceDecl"].includes(node.kind) || !Number.isSafeInteger(edit.major) || edit.major < 1) throw new Error("Semantic major requires a package/conformance and positive integer"); + if ( + !["conformanceDecl", "packageResourceDecl"].includes(node.kind) || + !Number.isSafeInteger(edit.major) || + edit.major < 1 + ) + throw new Error("Semantic major requires a package/conformance and positive integer"); const tokens = syntax.tokens.filter((token) => !token.trivia && token.start >= node.start && token.end <= node.end); const marker = tokens.findIndex((token) => token.kind === "SEMANTIC_MAJOR"); const value = marker < 0 ? undefined : tokens[marker + 1]; const brace = tokens.find((token) => token.kind === "LBRACE")!; - result = applySourceEdits(source, [{start: value?.start ?? brace.start, end: value?.end ?? brace.start, text: value ? String(edit.major) : `semantic-major ${edit.major} `}]); + result = applySourceEdits(source, [ + { + start: value?.start ?? brace.start, + end: value?.end ?? brace.start, + text: value ? String(edit.major) : `semantic-major ${edit.major} `, + }, + ]); } else if (edit.operation === "conformance-id") { - if (node.kind !== "conformanceDecl" || !edit.id) throw new Error("Identity enrollment requires a conformance and stable ID"); + if (node.kind !== "conformanceDecl" || !edit.id) + throw new Error("Identity enrollment requires a conformance and stable ID"); const existing = node.children.find((entry) => entry.kind === "stringLiteral"); if (existing) { - if (JSON.parse(source.slice(existing.start, existing.end)) !== edit.id) throw new Error("Cannot change an enrolled conformance identity; create a new conformance explicitly"); + if (JSON.parse(source.slice(existing.start, existing.end)) !== edit.id) + throw new Error("Cannot change an enrolled conformance identity; create a new conformance explicitly"); return source; } const identifiers = node.children.filter((entry) => entry.kind === "identifier"); const position = identifiers[identifiers.length - 1].end; - result = applySourceEdits(source, [{start: position, end: position, text: ` id ${JSON.stringify(edit.id)}`}]); + result = applySourceEdits(source, [{ start: position, end: position, text: ` id ${JSON.stringify(edit.id)}` }]); } else if (edit.operation === "append") { const closing = syntax.tokens.find((token) => token.kind === "RBRACE" && token.end === node.end); if (!closing) throw new Error("Append requires a declaration with a body"); - result = applySourceEdits(source, [{start: closing.start, end: closing.start, text: `\n${edit.source}\n`}]); + result = applySourceEdits(source, [{ start: closing.start, end: closing.start, text: `\n${edit.source}\n` }]); } else { // A resource parser node includes imports/external declarations preceding // its header. Replacing the declaration must not delete that preamble. - const identifier = node.children.find(child => child.kind === "identifier"); - const header = ["packageResourceDecl", "interfaceResourceDecl"].includes(node.kind) && identifier - ? syntax.tokens.filter(token => token.start >= node.start && token.end <= identifier.start && - token.kind === (node.kind === "packageResourceDecl" ? "PACKAGE" : "INTERFACE")).at(-1)?.start : undefined; - const wrapper = edit.operation === "remove" && ["stateDecl", "edgeDecl"].includes(node.kind) - ? [...walkSyntax(syntax.root)].filter((entry) => ["conformanceItem", "sharedAttachmentDecl"].includes(entry.kind) && entry.start <= node.start && entry.end >= node.end).sort((a, b) => (a.end - a.start) - (b.end - b.start))[0] - : undefined; - result = applySourceEdits(source, [{start: wrapper?.start ?? header ?? node.start, end: wrapper?.end ?? node.end, text: edit.operation === "replace" ? edit.source : ""}]); + const identifier = node.children.find((child) => child.kind === "identifier"); + const header = + ["packageResourceDecl", "interfaceResourceDecl"].includes(node.kind) && identifier + ? syntax.tokens + .filter( + (token) => + token.start >= node.start && + token.end <= identifier.start && + token.kind === (node.kind === "packageResourceDecl" ? "PACKAGE" : "INTERFACE"), + ) + .at(-1)?.start + : undefined; + const wrapper = + edit.operation === "remove" && ["stateDecl", "edgeDecl"].includes(node.kind) + ? [...walkSyntax(syntax.root)] + .filter( + (entry) => + ["conformanceItem", "sharedAttachmentDecl"].includes(entry.kind) && + entry.start <= node.start && + entry.end >= node.end, + ) + .sort((a, b) => a.end - a.start - (b.end - b.start))[0] + : undefined; + result = applySourceEdits(source, [ + { + start: wrapper?.start ?? header ?? node.start, + end: wrapper?.end ?? node.end, + text: edit.operation === "replace" ? edit.source : "", + }, + ]); } const checked = parseQx(result); - if (checked.diagnostics.length) throw new Error(`Invalid structural change: ${checked.diagnostics.map((entry) => entry.message).join("; ")}`); + if (checked.diagnostics.length) + throw new Error(`Invalid structural change: ${checked.diagnostics.map((entry) => entry.message).join("; ")}`); return result; }; export const scaffoldResourceSource = (kind: "interface" | "package", name: string, id: string, revision: string) => { - if (!/^[A-Z][A-Za-z0-9]*$/.test(name) || !id || !revision) throw new Error("Resource scaffold requires a PascalCase name and explicit identities"); + if (!/^[A-Z][A-Za-z0-9]*$/.test(name) || !id || !revision) + throw new Error("Resource scaffold requires a PascalCase name and explicit identities"); const source = `${kind} ${name} id ${JSON.stringify(id)} revision ${JSON.stringify(revision)} {\n}\n`; if (parseQx(source).diagnostics.length) throw new Error("Invalid resource scaffold"); return source; diff --git a/src/capability-language/structural-plan.ts b/src/capability-language/structural-plan.ts index d123309..e2ce3d3 100644 --- a/src/capability-language/structural-plan.ts +++ b/src/capability-language/structural-plan.ts @@ -7,125 +7,220 @@ import { editStructure, type StructuralEdit } from "./structural-edits.js"; import { snapshotRepository, localResourceSnapshots } from "./candidate-check.js"; import { compileWorkspaceRepository, compileCapabilityResourceRepository } from "./assembly.js"; import { createGitCapabilityResolver } from "./git-resolver.js"; -import {bindingSchema, generateTypeScriptBindings} from "../bindings/index.js"; +import { bindingSchema, generateTypeScriptBindings } from "../bindings/index.js"; import { parseQx } from "./source.js"; import { parseQuixosLockDocument } from "../resource-lock/index.js"; import { withFileLock } from "./file-lock.js"; export type StructuralRequest = { kind: "workspace" | "interface" | "package"; - source?: {repository: string; commit: string}; + source?: { repository: string; commit: string }; resourceRoot?: string; validation?: "syntax" | "resource-graph"; - files: ({file: string; edits: StructuralEdit[]} | {file: string; create: string} | {file: string; generated: string} | {file: string; expected: string; replace: string})[]; + files: ( + | { file: string; edits: StructuralEdit[] } + | { file: string; create: string } + | { file: string; generated: string } + | { file: string; expected: string; replace: string } + )[]; }; -type Change = {file: string; before: string | null; after: string; mode: number}; -type Journal = {schemaVersion: 1; id: string; root: string; phase: "prepared" | "complete"; changes: Change[]}; +type Change = { file: string; before: string | null; after: string; mode: number }; +type Journal = { schemaVersion: 1; id: string; root: string; phase: "prepared" | "complete"; changes: Change[] }; const safeFile = (file: string) => { - if (!/^(?:[A-Za-z0-9_-][A-Za-z0-9_.-]*\/)*(?:\.gitignore|\.yarnrc.yml|[A-Za-z0-9_-][A-Za-z0-9_.-]*\.(?:qx|lock|ts|tsx|css|mjs|json|nix|txtpb))$/.test(file) - || file.split("/").some((part) => [".git", ".jj", ".quixos", "node_modules"].includes(part))) throw new Error(`Unsafe scaffold path ${file}`); + if ( + !/^(?:[A-Za-z0-9_-][A-Za-z0-9_.-]*\/)*(?:\.gitignore|\.yarnrc.yml|[A-Za-z0-9_-][A-Za-z0-9_.-]*\.(?:qx|lock|ts|tsx|css|mjs|json|nix|txtpb))$/.test( + file, + ) || + file.split("/").some((part) => [".git", ".jj", ".quixos", "node_modules"].includes(part)) + ) + throw new Error(`Unsafe scaffold path ${file}`); }; const read = async (root: string, file: string): Promise => { safeFile(file); const target = path.join(root, file); try { const metadata = await fs.lstat(target); - if (!metadata.isFile() || metadata.isSymbolicLink() || !(await fs.realpath(target)).startsWith(`${root}/`)) throw new Error(`Scaffold target is not a contained regular file: ${file}`); + if (!metadata.isFile() || metadata.isSymbolicLink() || !(await fs.realpath(target)).startsWith(`${root}/`)) + throw new Error(`Scaffold target is not a contained regular file: ${file}`); return await fs.readFile(target, "utf8"); - } catch (error) { if ((error as NodeJS.ErrnoException).code === "ENOENT") return null; throw error; } + } catch (error) { + if ((error as NodeJS.ErrnoException).code === "ENOENT") return null; + throw error; + } }; const containedParent = async (root: string, file: string) => { let current = root; for (const part of file.split("/").slice(0, -1)) { current = path.join(current, part); - await fs.mkdir(current).catch((error: NodeJS.ErrnoException) => { if (error.code !== "EEXIST") throw error; }); + await fs.mkdir(current).catch((error: NodeJS.ErrnoException) => { + if (error.code !== "EEXIST") throw error; + }); const metadata = await fs.lstat(current); - if (!metadata.isDirectory() || metadata.isSymbolicLink()) throw new Error("Scaffold parent must be a real directory"); + if (!metadata.isDirectory() || metadata.isSymbolicLink()) + throw new Error("Scaffold parent must be a real directory"); } }; const durableJson = async (file: string, value: unknown) => { const temporary = `${file}.${randomUUID()}.tmp`; const handle = await fs.open(temporary, "wx", 0o600); - try { await handle.writeFile(`${JSON.stringify(value, null, 2)}\n`); await handle.sync(); } finally { await handle.close(); } + try { + await handle.writeFile(`${JSON.stringify(value, null, 2)}\n`); + await handle.sync(); + } finally { + await handle.close(); + } await fs.rename(temporary, file); const directory = await fs.open(path.dirname(file), "r"); - try { await directory.sync(); } finally { await directory.close(); } + try { + await directory.sync(); + } finally { + await directory.close(); + } }; /** Validate the entire edited resource graph in a private snapshot before writes. */ export const planStructure = async (rootPath: string, request: StructuralRequest, snapshotMap?: string) => { - if (request.validation && !["syntax", "resource-graph"].includes(request.validation)) throw new Error("Unknown structural validation mode"); + if (request.validation && !["syntax", "resource-graph"].includes(request.validation)) + throw new Error("Unknown structural validation mode"); const root = await fs.realpath(rootPath); const temporary = await fs.mkdtemp(path.join(os.tmpdir(), "qx-structure-")); try { const snapshot = await snapshotRepository(root, path.join(temporary, "source")); - const observed = await Promise.all(snapshot.files.map(async ({name}) => ({file: name, digest: contentDigest(await fs.readFile(path.join(snapshot.directory, name), "utf8"))}))); + const observed = await Promise.all( + snapshot.files.map(async ({ name }) => ({ + file: name, + digest: contentDigest(await fs.readFile(path.join(snapshot.directory, name), "utf8")), + })), + ); const changes: Change[] = []; - if (!Array.isArray(request.files) || !request.files.length || request.files.length > 100) throw new Error("Structural plan requires 1–100 files"); + if (!Array.isArray(request.files) || !request.files.length || request.files.length > 100) + throw new Error("Structural plan requires 1–100 files"); for (const input of request.files) { safeFile(input.file); if (changes.some((entry) => entry.file === input.file)) throw new Error("Repeated structural file target"); const before = await read(root, input.file); let after: string; if ("create" in input) { - if (before !== null || typeof input.create !== "string") throw new Error("Scaffold creation cannot replace an existing file"); + if (before !== null || typeof input.create !== "string") + throw new Error("Scaffold creation cannot replace an existing file"); after = input.create; } else if ("replace" in input) { - if (before !== input.expected || typeof input.replace !== "string") throw new Error(`Stale imperative edit: ${input.file}`); + if (before !== input.expected || typeof input.replace !== "string") + throw new Error(`Stale imperative edit: ${input.file}`); after = input.replace; } else if ("generated" in input) { - const generated = (text: string) => text.startsWith("// Generated by qx-scaffold-v1\n") || text.startsWith("# Generated by qx-scaffold-v1\n") || (() => {try {return JSON.parse(text).generatedBy === "qx-scaffold-v1";} catch {return false;}})(); - if (typeof input.generated !== "string" || !generated(input.generated) || (before !== null && !generated(before))) throw new Error("Only scaffold-owned generated files may be regenerated"); + const generated = (text: string) => + text.startsWith("// Generated by qx-scaffold-v1\n") || + text.startsWith("# Generated by qx-scaffold-v1\n") || + (() => { + try { + return JSON.parse(text).generatedBy === "qx-scaffold-v1"; + } catch { + return false; + } + })(); + if ( + typeof input.generated !== "string" || + !generated(input.generated) || + (before !== null && !generated(before)) + ) + throw new Error("Only scaffold-owned generated files may be regenerated"); after = input.generated; } else { - if (before === null || !Array.isArray(input.edits)) throw new Error("Structural edit requires an existing source"); + if (before === null || !Array.isArray(input.edits)) + throw new Error("Structural edit requires an existing source"); after = input.edits.reduce(editStructure, before); } if (Buffer.byteLength(after) > 1024 * 1024) throw new Error("Scaffold file exceeds 1 MiB"); const mode = before === null ? 0o644 : (await fs.stat(path.join(root, input.file))).mode & 0o777; - changes.push({file: input.file, before, after, mode}); + changes.push({ file: input.file, before, after, mode }); await containedParent(snapshot.directory, input.file); await fs.writeFile(path.join(snapshot.directory, input.file), after); } if (request.validation === "syntax") { for (const change of changes) { - if (change.file.endsWith(".qx") && parseQx(change.after, change.file).diagnostics.length) throw new Error(`Invalid QX syntax in ${change.file}`); - if (change.file.endsWith(".lock") && !parseQuixosLockDocument(change.after, change.file).ok) throw new Error(`Invalid lock syntax in ${change.file}`); + if (change.file.endsWith(".qx") && parseQx(change.after, change.file).diagnostics.length) + throw new Error(`Invalid QX syntax in ${change.file}`); + if (change.file.endsWith(".lock") && !parseQuixosLockDocument(change.after, change.file).ok) + throw new Error(`Invalid lock syntax in ${change.file}`); } } else { - const localMap = path.join(temporary, "local-resources.json"); - await fs.writeFile(localMap, JSON.stringify(await localResourceSnapshots(root, snapshotMap))); - const resolveResource = await createGitCapabilityResolver({checkoutRoot: path.join(temporary, "resources"), snapshotMap: localMap}); - if (request.resourceRoot && !/^[A-Za-z0-9_-][A-Za-z0-9_.-]*(?:\/[A-Za-z0-9_-][A-Za-z0-9_.-]*)*$/.test(request.resourceRoot)) throw new Error("Resource root must be a contained relative directory"); - const resourceRoot = path.join(snapshot.directory, request.resourceRoot ?? ""); - if (request.kind === "workspace") await compileWorkspaceRepository({rootDirectory: resourceRoot, resolveResource}); - else if (["package", "interface"].includes(request.kind) && request.source) { - const compiled = await compileCapabilityResourceRepository({rootDirectory: resourceRoot, kind: request.kind as "package" | "interface", source: {resolver: "git", ...request.source}, resolveResource}); - if (compiled.resource.kind === "package") { - const configuration = JSON.parse(await fs.readFile(path.join(resourceRoot, "quixos.check.json"), "utf8")); - const artifacts = [ - {file: configuration.bindingOutput as string, after: generateTypeScriptBindings(bindingSchema(compiled), compiled.resource.revision.revisionId, configuration.options)}, - ]; - for (const artifact of artifacts) { - const file = request.resourceRoot ? `${request.resourceRoot}/${artifact.file}` : artifact.file; - safeFile(file); - if (Buffer.byteLength(artifact.after) > 1024 * 1024) throw new Error("Generated scaffold file exceeds 1 MiB"); - const before = await read(root, file); - if (before !== null && !before.startsWith("// Generated by quixos-codegen-ts.") && (() => {try {return JSON.parse(before).generatedBy !== "qx-scaffold-v1";} catch {return true;}})()) throw new Error(`Refusing to overwrite hand-authored generated artifact ${file}`); - const previous = changes.find((entry) => entry.file === file); - if (previous) previous.after = artifact.after; - else changes.push({file, before, after: artifact.after, mode: 0o644}); + const localMap = path.join(temporary, "local-resources.json"); + await fs.writeFile(localMap, JSON.stringify(await localResourceSnapshots(root, snapshotMap))); + const resolveResource = await createGitCapabilityResolver({ + checkoutRoot: path.join(temporary, "resources"), + snapshotMap: localMap, + }); + if ( + request.resourceRoot && + !/^[A-Za-z0-9_-][A-Za-z0-9_.-]*(?:\/[A-Za-z0-9_-][A-Za-z0-9_.-]*)*$/.test(request.resourceRoot) + ) + throw new Error("Resource root must be a contained relative directory"); + const resourceRoot = path.join(snapshot.directory, request.resourceRoot ?? ""); + if (request.kind === "workspace") + await compileWorkspaceRepository({ rootDirectory: resourceRoot, resolveResource }); + else if (["package", "interface"].includes(request.kind) && request.source) { + const compiled = await compileCapabilityResourceRepository({ + rootDirectory: resourceRoot, + kind: request.kind as "package" | "interface", + source: { resolver: "git", ...request.source }, + resolveResource, + }); + if (compiled.resource.kind === "package") { + const configuration = JSON.parse(await fs.readFile(path.join(resourceRoot, "quixos.check.json"), "utf8")); + const artifacts = [ + { + file: configuration.bindingOutput as string, + after: generateTypeScriptBindings( + bindingSchema(compiled), + compiled.resource.revision.revisionId, + configuration.options, + ), + }, + ]; + for (const artifact of artifacts) { + const file = request.resourceRoot ? `${request.resourceRoot}/${artifact.file}` : artifact.file; + safeFile(file); + if (Buffer.byteLength(artifact.after) > 1024 * 1024) + throw new Error("Generated scaffold file exceeds 1 MiB"); + const before = await read(root, file); + if ( + before !== null && + !before.startsWith("// Generated by quixos-codegen-ts.") && + (() => { + try { + return JSON.parse(before).generatedBy !== "qx-scaffold-v1"; + } catch { + return true; + } + })() + ) + throw new Error(`Refusing to overwrite hand-authored generated artifact ${file}`); + const previous = changes.find((entry) => entry.file === file); + if (previous) previous.after = artifact.after; + else changes.push({ file, before, after: artifact.after, mode: 0o644 }); + } } - } - } - else throw new Error("Resource plans require kind and exact authored source identity"); + } else throw new Error("Resource plans require kind and exact authored source identity"); } if (changes.length > 100) throw new Error("Structural plan including generated artifacts exceeds 100 files"); // Validation may fetch dependencies; reject edits made while it was running. - for (const entry of changes) if (await read(root, entry.file) !== entry.before) throw new Error(`Source changed while planning: ${entry.file}`); - for (const entry of observed) if (contentDigest(await fs.readFile(path.join(root, entry.file), "utf8")) !== entry.digest) throw new Error(`Validation input changed while planning: ${entry.file}`); - return {root, changes, observed, digest: contentDigest(changes), validation: request.validation ?? "resource-graph" as const}; - } finally { await fs.rm(temporary, {recursive: true, force: true}); } + for (const entry of changes) + if ((await read(root, entry.file)) !== entry.before) + throw new Error(`Source changed while planning: ${entry.file}`); + for (const entry of observed) + if (contentDigest(await fs.readFile(path.join(root, entry.file), "utf8")) !== entry.digest) + throw new Error(`Validation input changed while planning: ${entry.file}`); + return { + root, + changes, + observed, + digest: contentDigest(changes), + validation: request.validation ?? ("resource-graph" as const), + }; + } finally { + await fs.rm(temporary, { recursive: true, force: true }); + } }; /** Replay only exact before/after states. A crash never loses the original text. */ @@ -134,33 +229,47 @@ const replayStructure = async (rootPath: string, id: string) => { if (!/^[a-f0-9-]{36}$/.test(id)) throw new Error("Invalid scaffold journal ID"); const journalPath = path.join(root, ".quixos", "scaffolds", `${id}.json`); const journal = JSON.parse(await fs.readFile(journalPath, "utf8")) as Journal; - if (journal.schemaVersion !== 1 || journal.root !== root || journal.id !== id) throw new Error("Scaffold journal identity mismatch"); + if (journal.schemaVersion !== 1 || journal.root !== root || journal.id !== id) + throw new Error("Scaffold journal identity mismatch"); for (const entry of journal.changes) { const current = await read(root, entry.file); - if (current !== entry.before && current !== entry.after) throw new Error(`Scaffold conflicts with newer edits: ${entry.file}; original text is retained in ${journalPath}`); + if (current !== entry.before && current !== entry.after) + throw new Error( + `Scaffold conflicts with newer edits: ${entry.file}; original text is retained in ${journalPath}`, + ); } - if (journal.phase === "complete") return {id, journalPath, phase: journal.phase}; + if (journal.phase === "complete") return { id, journalPath, phase: journal.phase }; for (const entry of journal.changes) { - if (await read(root, entry.file) === entry.after) continue; + if ((await read(root, entry.file)) === entry.after) continue; await containedParent(root, entry.file); const target = path.join(root, entry.file); const temporary = `${target}.qx-${randomUUID()}.tmp`; const handle = await fs.open(temporary, "wx", entry.mode); - try { await handle.writeFile(entry.after); await handle.sync(); } finally { await handle.close(); } + try { + await handle.writeFile(entry.after); + await handle.sync(); + } finally { + await handle.close(); + } if (entry.before === null) { // link is atomic and fails if another author created the destination. await fs.link(temporary, target); await fs.unlink(temporary); } else { - if (await read(root, entry.file) !== entry.before) throw new Error(`Source changed during scaffold: ${entry.file}`); + if ((await read(root, entry.file)) !== entry.before) + throw new Error(`Source changed during scaffold: ${entry.file}`); await fs.rename(temporary, target); } const directory = await fs.open(path.dirname(target), "r"); - try { await directory.sync(); } finally { await directory.close(); } + try { + await directory.sync(); + } finally { + await directory.close(); + } } journal.phase = "complete"; await durableJson(journalPath, journal); - return {id, journalPath, phase: journal.phase}; + return { id, journalPath, phase: journal.phase }; }; const withStructureLock = async (root: string, work: () => Promise) => { @@ -172,22 +281,41 @@ export const resumeStructure = async (rootPath: string, id: string) => { const root = await fs.realpath(rootPath); return withStructureLock(root, () => replayStructure(root, id)); }; -export const applyStructure = async (plan: Awaited>, id: string = randomUUID()) => withStructureLock(plan.root, async () => { - if (!/^[a-f0-9-]{36}$/.test(id)) throw new Error("Invalid scaffold journal ID"); - const directory = path.join(plan.root, ".quixos", "scaffolds"); - try { - const existing = JSON.parse(await fs.readFile(path.join(directory, `${id}.json`), "utf8")) as Journal; - if (existing.root !== plan.root || contentDigest(existing.changes) !== plan.digest) throw new Error("Scaffold journal identity conflict"); - for (const entry of plan.observed) if (!plan.changes.some((change) => change.file === entry.file) && contentDigest(await fs.readFile(path.join(plan.root, entry.file), "utf8")) !== entry.digest) throw new Error(`Stale scaffold validation input: ${entry.file}`); +export const applyStructure = async (plan: Awaited>, id: string = randomUUID()) => + withStructureLock(plan.root, async () => { + if (!/^[a-f0-9-]{36}$/.test(id)) throw new Error("Invalid scaffold journal ID"); + const directory = path.join(plan.root, ".quixos", "scaffolds"); + try { + const existing = JSON.parse(await fs.readFile(path.join(directory, `${id}.json`), "utf8")) as Journal; + if (existing.root !== plan.root || contentDigest(existing.changes) !== plan.digest) + throw new Error("Scaffold journal identity conflict"); + for (const entry of plan.observed) + if ( + !plan.changes.some((change) => change.file === entry.file) && + contentDigest(await fs.readFile(path.join(plan.root, entry.file), "utf8")) !== entry.digest + ) + throw new Error(`Stale scaffold validation input: ${entry.file}`); + return replayStructure(plan.root, id); + } catch (error) { + if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; + } + // An unfinished journal must be recovered before another structural mutation. + for (const file of await fs.readdir(directory)) + if (file.endsWith(".json")) { + const prior = JSON.parse(await fs.readFile(path.join(directory, file), "utf8")) as Journal; + if (prior.phase !== "complete") throw new Error(`Unfinished scaffold ${prior.id}; resume it first`); + } + for (const entry of plan.changes) + if ((await read(plan.root, entry.file)) !== entry.before) throw new Error(`Stale scaffold plan: ${entry.file}`); + for (const entry of plan.observed) + if (contentDigest(await fs.readFile(path.join(plan.root, entry.file), "utf8")) !== entry.digest) + throw new Error(`Stale scaffold validation input: ${entry.file}`); + await durableJson(path.join(directory, `${id}.json`), { + schemaVersion: 1, + id, + root: plan.root, + phase: "prepared", + changes: plan.changes, + } satisfies Journal); return replayStructure(plan.root, id); - } catch (error) {if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;} - // An unfinished journal must be recovered before another structural mutation. - for (const file of await fs.readdir(directory)) if (file.endsWith(".json")) { - const prior = JSON.parse(await fs.readFile(path.join(directory, file), "utf8")) as Journal; - if (prior.phase !== "complete") throw new Error(`Unfinished scaffold ${prior.id}; resume it first`); - } - for (const entry of plan.changes) if (await read(plan.root, entry.file) !== entry.before) throw new Error(`Stale scaffold plan: ${entry.file}`); - for (const entry of plan.observed) if (contentDigest(await fs.readFile(path.join(plan.root, entry.file), "utf8")) !== entry.digest) throw new Error(`Stale scaffold validation input: ${entry.file}`); - await durableJson(path.join(directory, `${id}.json`), {schemaVersion: 1, id, root: plan.root, phase: "prepared", changes: plan.changes} satisfies Journal); - return replayStructure(plan.root, id); -}); + }); diff --git a/src/capability-language/tool-cli.ts b/src/capability-language/tool-cli.ts index 7a6cae1..feae14a 100644 --- a/src/capability-language/tool-cli.ts +++ b/src/capability-language/tool-cli.ts @@ -2,21 +2,21 @@ import { readFile, writeFile, mkdtemp, rm, realpath } from "node:fs/promises"; import { parseQx, formatQx, lintQx } from "./source.js"; import { scaffoldAtom } from "./scaffold.js"; -import {loadQxSources} from "./source-loader.js"; +import { loadQxSources } from "./source-loader.js"; import { createGitCapabilityResolver } from "./git-resolver.js"; import { planEvolution } from "../capability-model/index.js"; import { snapshotRepository } from "./candidate-check.js"; -import {planPinUpgrades, applyPinUpgrades, discoverUpgradeSpec, type UpgradeSpec} from "./pin-upgrades.js"; +import { planPinUpgrades, applyPinUpgrades, discoverUpgradeSpec, type UpgradeSpec } from "./pin-upgrades.js"; import path from "node:path"; import os from "node:os"; -import {spawnSync} from "node:child_process"; +import { spawnSync } from "node:child_process"; import { planStructure, applyStructure, resumeStructure, type StructuralRequest } from "./structural-plan.js"; -import {scaffoldRecipe, type ScaffoldRecipe} from "./scaffold-recipes.js"; -import {checkBundleSources} from "../bindings/bundle-policy.js"; -import {sealMigrations} from "./migration-seal.js"; -import {generatePackageDescriptor} from "../bindings/index.js"; -import {buildCheckedPackage, buildImmutableCandidate, snapshotCommit} from "./checked-build.js"; -import {formatQuixosLock, loadQuixosLock, parseQuixosLockDocument} from "../resource-lock/index.js"; +import { scaffoldRecipe, type ScaffoldRecipe } from "./scaffold-recipes.js"; +import { checkBundleSources } from "../bindings/bundle-policy.js"; +import { sealMigrations } from "./migration-seal.js"; +import { generatePackageDescriptor } from "../bindings/index.js"; +import { buildCheckedPackage, buildImmutableCandidate, snapshotCommit } from "./checked-build.js"; +import { formatQuixosLock, loadQuixosLock, parseQuixosLockDocument } from "../resource-lock/index.js"; import { walkSyntax } from "./source.js"; import { inspectWorkbench } from "./authoring-inspect.js"; import { authoringContext } from "./authoring-context.js"; @@ -25,18 +25,30 @@ import { checkAuthoring } from "./authoring-check.js"; import { authoringWorklist } from "./authoring-worklist.js"; const authorSource = async (root: string) => { - const result = spawnSync("git", ["config", "--get", "remote.origin.url"], {cwd: root, encoding: "utf8"}); + const result = spawnSync("git", ["config", "--get", "remote.origin.url"], { cwd: root, encoding: "utf8" }); if (result.error || result.status !== 0) throw new Error("Managed resource has no origin"); - return {repository: result.stdout.trim(), commit: await snapshotCommit(root)}; + return { repository: result.stdout.trim(), commit: await snapshotCommit(root) }; }; const readSpec = async (value: string) => { - if (value === "-") { let input = ""; for await (const chunk of process.stdin) { input += chunk; if (input.length > 1024 * 1024) throw new Error("Scaffold specification exceeds 1 MiB"); } return JSON.parse(input); } + if (value === "-") { + let input = ""; + for await (const chunk of process.stdin) { + input += chunk; + if (input.length > 1024 * 1024) throw new Error("Scaffold specification exceeds 1 MiB"); + } + return JSON.parse(input); + } return JSON.parse(value.trimStart().startsWith("{") ? value : await readFile(value, "utf8")); }; const planSummary = (plan: Awaited>) => ({ - root: plan.root, validation: plan.validation, - changes: plan.changes.map(change => ({file: change.file, beforeBytes: change.before?.length ?? 0, afterBytes: change.after?.length ?? 0})), + root: plan.root, + validation: plan.validation, + changes: plan.changes.map((change) => ({ + file: change.file, + beforeBytes: change.before?.length ?? 0, + afterBytes: change.after?.length ?? 0, + })), note: "Structural plan only, not implementation verification. Run qx-workspace check while iterating.", }); @@ -47,43 +59,62 @@ const main = async () => { const sources = JSON.parse(args[0]); if (!Array.isArray(sources) || sources.length > 100) throw new Error("Expected at most 100 scaffold sources"); for (const entry of sources) { - if (!entry || typeof entry.file !== "string" || typeof entry.source !== "string" || entry.source.length > 1024*1024) throw new Error("Invalid scaffold source"); + if ( + !entry || + typeof entry.file !== "string" || + typeof entry.source !== "string" || + entry.source.length > 1024 * 1024 + ) + throw new Error("Invalid scaffold source"); const diagnostics = parseQx(entry.source, entry.file).diagnostics; - if (diagnostics.length) throw new Error(diagnostics.map(d => `${entry.file}:${d.line}:${d.column + 1}: ${d.message}`).join("\n")); + if (diagnostics.length) + throw new Error(diagnostics.map((d) => `${entry.file}:${d.line}:${d.column + 1}: ${d.message}`).join("\n")); } process.stdout.write('{"syntaxValid":true,"verificationEvidence":false}\n'); return; } if (command === "scaffold-placement-binding") { if (args.length !== 1) throw new Error("scaffold-placement-binding ROOT"); - const {source} = await loadQxSources(args[0]); + const { source } = await loadQxSources(args[0]); const syntax = parseQx(source); - const text = (n: {start: number; end: number}) => source.slice(n.start, n.end); + const text = (n: { start: number; end: number }) => source.slice(n.start, n.end); const matches: string[] = []; for (const edge of walkSyntax(syntax.root)) { if (edge.kind !== "edgeDecl") continue; - for (const endpoint of edge.children.filter(n => n.kind === "edgeEndpoint")) { - const target = endpoint.children.find(n => n.kind === "targetConstraint"); - if (!target || !syntax.tokens.some(t => t.start >= target.start && t.end <= target.end && t.kind === "INTERFACE") || - !target.children.some(n => n.kind === "identifier" && text(n) === "WebStudioPlaceable")) continue; - const edgeName = text(edge.children.find(n => n.kind === "identifier")!); - const projection = text(endpoint.children.find(n => n.kind === "identifier")!); + for (const endpoint of edge.children.filter((n) => n.kind === "edgeEndpoint")) { + const target = endpoint.children.find((n) => n.kind === "targetConstraint"); + if ( + !target || + !syntax.tokens.some((t) => t.start >= target.start && t.end <= target.end && t.kind === "INTERFACE") || + !target.children.some((n) => n.kind === "identifier" && text(n) === "WebStudioPlaceable") + ) + continue; + const edgeName = text(edge.children.find((n) => n.kind === "identifier")!); + const projection = text(endpoint.children.find((n) => n.kind === "identifier")!); matches.push(`bind placements.resolve to edge ${edgeName}.${projection}.resolve;`); } } - if (matches.length !== 1) throw new Error(`Expected one canvas placement edge for WebStudioPlaceable; found ${matches.length}. Configure the workspace canvas before adding a bundle.`); - process.stdout.write(JSON.stringify({binding: matches[0]}) + "\n"); + if (matches.length !== 1) + throw new Error( + `Expected one canvas placement edge for WebStudioPlaceable; found ${matches.length}. Configure the workspace canvas before adding a bundle.`, + ); + process.stdout.write(JSON.stringify({ binding: matches[0] }) + "\n"); return; } if (command === "package-identity") { if (args.length !== 1) throw new Error("usage: quixos-qx package-identity PACKAGE_QX"); const authored = await readFile(args[0], "utf8"); const syntax = parseQx(authored); - const declarations = [...walkSyntax(syntax.root)].filter(node => node.kind === "packageResourceDecl"); - if (syntax.diagnostics.length || declarations.length !== 1) throw new Error("Expected one valid package declaration"); - const literals = declarations[0].children.filter(node => node.kind === "stringLiteral"); - process.stdout.write(JSON.stringify({id: JSON.parse(authored.slice(literals[0].start, literals[0].end)), - revision: JSON.parse(authored.slice(literals[1].start, literals[1].end))}) + "\n"); + const declarations = [...walkSyntax(syntax.root)].filter((node) => node.kind === "packageResourceDecl"); + if (syntax.diagnostics.length || declarations.length !== 1) + throw new Error("Expected one valid package declaration"); + const literals = declarations[0].children.filter((node) => node.kind === "stringLiteral"); + process.stdout.write( + JSON.stringify({ + id: JSON.parse(authored.slice(literals[0].start, literals[0].end)), + revision: JSON.parse(authored.slice(literals[1].start, literals[1].end)), + }) + "\n", + ); return; } if (command === "package-descriptor") { @@ -108,29 +139,50 @@ const main = async () => { } if (["author-check", "author-contract"].includes(command) && !args.includes("--help")) { const [root, output, ...flags] = args; - if (!root || !output) throw new Error("usage: quixos-qx author-check ROOT OUTPUT [--baseline FILE] [--reviews FILE]"); - const options: {baseline?: string; reviews?: string} = {}; + if (!root || !output) + throw new Error("usage: quixos-qx author-check ROOT OUTPUT [--baseline FILE] [--reviews FILE]"); + const options: { baseline?: string; reviews?: string } = {}; for (let index = 0; index < flags.length; index += 2) { if (!flags[index + 1]) throw new Error("Missing check option value"); if (flags[index] === "--baseline") options.baseline = flags[index + 1]; else if (flags[index] === "--reviews") options.reviews = flags[index + 1]; else throw new Error(`Unknown check option ${flags[index]}`); } - const result = await checkAuthoring(root, output, {...options, contractOnly: command === "author-contract"}); + const result = await checkAuthoring(root, output, { ...options, contractOnly: command === "author-contract" }); process.stdout.write(`${JSON.stringify(result, null, 2)}\n`); if (result.blockers.length) process.exitCode = 1; return; } if (["converge", "_converge"].includes(command) && !args.includes("--help")) { - if (!args.length || args.length > 2) throw new Error("usage: quixos-qx converge WORKBENCH [REGISTERED_DIRECTORY] (join package writers first)"); + if (!args.length || args.length > 2) + throw new Error("usage: quixos-qx converge WORKBENCH [REGISTERED_DIRECTORY] (join package writers first)"); const context = await authoringContext(args[0]); if (command === "converge") { console.error(`[${new Date().toISOString()}] Capture: waiting for coordinator lock (120s limit)`); - const result = spawnSync("flock", ["--exclusive", "--timeout", "120", "--conflict-exit-code", "75", path.join(context.workbench, ".quixos/converge.lock"), - process.execPath, process.argv[1], "_converge", context.workbench, ...(args[1] ? [args[1]] : [])], {stdio: "inherit"}); + const result = spawnSync( + "flock", + [ + "--exclusive", + "--timeout", + "120", + "--conflict-exit-code", + "75", + path.join(context.workbench, ".quixos/converge.lock"), + process.execPath, + process.argv[1], + "_converge", + context.workbench, + ...(args[1] ? [args[1]] : []), + ], + { stdio: "inherit" }, + ); if (result.error) throw result.error; - if (result.status === 75) process.stderr.write("Timed out after 120 seconds waiting for source capture; inspect the active coordinator. No build lock is held.\n"); - process.exitCode = result.status ?? 1; return; + if (result.status === 75) + process.stderr.write( + "Timed out after 120 seconds waiting for source capture; inspect the active coordinator. No build lock is held.\n", + ); + process.exitCode = result.status ?? 1; + return; } console.error(`[${new Date().toISOString()}] Capture: coordinator lock acquired`); const result = await convergeAuthoring(context.workbench, args[1]); @@ -140,18 +192,24 @@ const main = async () => { } if (command === "check-committed" && !args.includes("--help")) { const [kind, repository, commit, log, ...extra] = args; - if (!["workspace", "interface", "package"].includes(kind) || !log || extra.length) throw new Error("usage: quixos-qx check-committed workspace|interface|package REPOSITORY COMMIT LOG_FILE"); - process.stdout.write(`${await buildImmutableCandidate({repository, commit}, kind as "workspace" | "interface" | "package", log)}\n`); + if (!["workspace", "interface", "package"].includes(kind) || !log || extra.length) + throw new Error("usage: quixos-qx check-committed workspace|interface|package REPOSITORY COMMIT LOG_FILE"); + process.stdout.write( + `${await buildImmutableCandidate({ repository, commit }, kind as "workspace" | "interface" | "package", log)}\n`, + ); return; } if (!command || command === "--help" || args.includes("--help")) { - process.stdout.write("quixos-qx: author-check, author-contract, converge, worklist, inspect, resources, check-committed, source-baseline, scaffold-package, scaffold-interface, scaffold-function, scaffold-dependency, scaffold-structure, scaffold-resume, pin-upgrade, parse, lint, format\n" + - "inspect WORKBENCH [RESOURCE] shows provisional contracts, with explicit historical fallback; never verification evidence.\n" + - "resources WORKBENCH lists registered editable repositories. Use qx-workspace for the workspace authoring workflow.\n"); + process.stdout.write( + "quixos-qx: author-check, author-contract, converge, worklist, inspect, resources, check-committed, source-baseline, scaffold-package, scaffold-interface, scaffold-function, scaffold-dependency, scaffold-structure, scaffold-resume, pin-upgrade, parse, lint, format\n" + + "inspect WORKBENCH [RESOURCE] shows provisional contracts, with explicit historical fallback; never verification evidence.\n" + + "resources WORKBENCH lists registered editable repositories. Use qx-workspace for the workspace authoring workflow.\n", + ); return; } if (command === "inspect" || command === "resources") { - if (!args[0] || args.length > (command === "inspect" ? 2 : 1)) throw new Error(`usage: quixos-qx ${command} WORKBENCH${command === "inspect" ? " [RESOURCE]" : ""}`); + if (!args[0] || args.length > (command === "inspect" ? 2 : 1)) + throw new Error(`usage: quixos-qx ${command} WORKBENCH${command === "inspect" ? " [RESOURCE]" : ""}`); const result = command === "inspect" ? await inspectWorkbench(args[0], args[1]) : await authoringContext(args[0]); process.stdout.write(`${JSON.stringify(result, null, 2)}\n`); return; @@ -165,27 +223,56 @@ const main = async () => { const [root, kind, name, ...remaining] = args; let repository: string | undefined, commit: string | undefined; const flags = [...remaining]; - if (flags.length && !flags[0].startsWith("--")) { repository = flags.shift(); commit = flags.shift(); } - else if (root && ["interface", "package"].includes(kind) && name) { + if (flags.length && !flags[0].startsWith("--")) { + repository = flags.shift(); + commit = flags.shift(); + } else if (root && ["interface", "package"].includes(kind) && name) { const context = await authoringContext(root); const alias = await realpath(path.join(context.workbench, `${kind}s`, name)).catch(() => null); - const matches = context.resources.filter(entry => entry.kind === kind && (entry.resourceId === name || path.basename(entry.directory) === name || path.join(context.workbench, entry.directory) === alias)); - if (matches.length !== 1 || !matches[0].source) throw new Error(`Select exactly one registered ${kind} with qx-workspace resources; no match for ${name}`); + const matches = context.resources.filter( + (entry) => + entry.kind === kind && + (entry.resourceId === name || + path.basename(entry.directory) === name || + path.join(context.workbench, entry.directory) === alias), + ); + if (matches.length !== 1 || !matches[0].source) + throw new Error(`Select exactly one registered ${kind} with qx-workspace resources; no match for ${name}`); const selected = matches[0]; let source = selected.source!; if (flags.includes("--write")) { - const retained = spawnSync("quixos-qx", ["converge", context.workbench, selected.directory], {encoding: "utf8", env: {...process.env, QUIXOS_JJ_NO_CHECKPOINT: "1"}}); - if (retained.error || retained.status !== 0) throw new Error(`Dependency source needs attention: ${retained.error?.message ?? retained.stdout ?? retained.stderr}`); + const retained = spawnSync("quixos-qx", ["converge", context.workbench, selected.directory], { + encoding: "utf8", + env: { ...process.env, QUIXOS_JJ_NO_CHECKPOINT: "1" }, + }); + if (retained.error || retained.status !== 0) + throw new Error( + `Dependency source needs attention: ${retained.error?.message ?? retained.stdout ?? retained.stderr}`, + ); source = JSON.parse(retained.stdout).candidate; } - repository = source.repository; commit = source.commit; + repository = source.repository; + commit = source.commit; } - if (!root || !["interface", "package"].includes(kind) || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(name ?? "") || !repository || !commit || flags.some(flag => flag !== "--write")) throw new Error("usage: quixos-qx scaffold-dependency ROOT interface|package NAME REPOSITORY COMMIT [--write]"); + if ( + !root || + !["interface", "package"].includes(kind) || + !/^[A-Za-z_][A-Za-z0-9_]*$/.test(name ?? "") || + !repository || + !commit || + flags.some((flag) => flag !== "--write") + ) + throw new Error("usage: quixos-qx scaffold-dependency ROOT interface|package NAME REPOSITORY COMMIT [--write]"); const resourceKind = kind as "package" | "interface"; let entrypoint: "workspace" | "package" | "interface" | undefined; for (const candidate of ["workspace", "package", "interface"] as const) { - try {await readFile(path.join(root, `${candidate}.qx`)); if (entrypoint) throw new Error("Ambiguous repository entrypoint"); entrypoint = candidate;} - catch (error) {if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;} + try { + await readFile(path.join(root, `${candidate}.qx`)); + if (entrypoint) throw new Error("Ambiguous repository entrypoint"); + entrypoint = candidate; + } catch (error) { + if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; + } } if (!entrypoint) throw new Error("No QX repository entrypoint"); const lock = await loadQuixosLock(path.join(root, "quixos.lock")); @@ -193,45 +280,84 @@ const main = async () => { let target = "quixos.lock"; for (const file of lock.lock.sourceFiles ?? ["quixos.lock"]) { const parsed = parseQuixosLockDocument(await readFile(path.join(root, file), "utf8")); - if (parsed.ok && parsed.document.resources.some(entry => entry.kind === kind && entry.binding === name)) target = file; + if (parsed.ok && parsed.document.resources.some((entry) => entry.kind === kind && entry.binding === name)) + target = file; } - const request: StructuralRequest = {kind: entrypoint, source: await authorSource(root), validation: "syntax", files: [ - {file: `${entrypoint}.qx`, edits: [{operation: "import", kind: resourceKind, name}]}, - {file: target, edits: [{operation: "dependency", kind: resourceKind, name, source: {repository, commit}}]}, - ]}; + const request: StructuralRequest = { + kind: entrypoint, + source: await authorSource(root), + validation: "syntax", + files: [ + { file: `${entrypoint}.qx`, edits: [{ operation: "import", kind: resourceKind, name }] }, + { + file: target, + edits: [{ operation: "dependency", kind: resourceKind, name, source: { repository, commit } }], + }, + ], + }; const plan = await planStructure(root, request, process.env.QUIXOS_SNAPSHOT_MAP); - process.stdout.write(`${JSON.stringify({...planSummary(plan), applied: flags.includes("--write") ? await applyStructure(plan) : undefined}, null, 2)}\n`); + process.stdout.write( + `${JSON.stringify({ ...planSummary(plan), applied: flags.includes("--write") ? await applyStructure(plan) : undefined }, null, 2)}\n`, + ); return; } if (command === "scaffold-interface") { const [root, specFile, ...flags] = args; - if (!root || !specFile || flags.some(flag => flag !== "--write")) throw new Error("usage: quixos-qx scaffold-interface ROOT SPEC_JSON [--write]"); - const spec = await readSpec(specFile) as ScaffoldRecipe; - if (!spec.name || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(spec.name) || !spec.id || !spec.revision || !spec.tools?.quixos) throw new Error("Interface scaffold requires name, id, revision and Quixos toolchain source"); - const request: StructuralRequest = {kind: "interface", source: spec.source, files: [ - {file: "interface.qx", create: spec.declaration ?? `interface ${spec.name} id ${JSON.stringify(spec.id)} revision ${JSON.stringify(spec.revision)} {\n}\n`}, - {file: "quixos.lock", create: formatQuixosLock({formatVersion: 1, quixos: {resolver: "git", ...spec.tools.quixos}, resources: []})}, - {file: ".gitignore", create: ".quixos/\n"}, - ]}; + if (!root || !specFile || flags.some((flag) => flag !== "--write")) + throw new Error("usage: quixos-qx scaffold-interface ROOT SPEC_JSON [--write]"); + const spec = (await readSpec(specFile)) as ScaffoldRecipe; + if (!spec.name || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(spec.name) || !spec.id || !spec.revision || !spec.tools?.quixos) + throw new Error("Interface scaffold requires name, id, revision and Quixos toolchain source"); + const request: StructuralRequest = { + kind: "interface", + source: spec.source, + files: [ + { + file: "interface.qx", + create: + spec.declaration ?? + `interface ${spec.name} id ${JSON.stringify(spec.id)} revision ${JSON.stringify(spec.revision)} {\n}\n`, + }, + { + file: "quixos.lock", + create: formatQuixosLock({ + formatVersion: 1, + quixos: { resolver: "git", ...spec.tools.quixos }, + resources: [], + }), + }, + { file: ".gitignore", create: ".quixos/\n" }, + ], + }; const plan = await planStructure(root, request, process.env.QUIXOS_SNAPSHOT_MAP); - process.stdout.write(`${JSON.stringify({...planSummary(plan), applied: flags.includes("--write") ? await applyStructure(plan) : undefined}, null, 2)}\n`); + process.stdout.write( + `${JSON.stringify({ ...planSummary(plan), applied: flags.includes("--write") ? await applyStructure(plan) : undefined }, null, 2)}\n`, + ); return; } if (command === "build-package") { - if (args.length !== 3) throw new Error("usage: quixos-qx build-package COMMITTED_SOURCE SCHEMA PACKAGE_REVISION_ID"); + if (args.length !== 3) + throw new Error("usage: quixos-qx build-package COMMITTED_SOURCE SCHEMA PACKAGE_REVISION_ID"); process.stdout.write(`${await buildCheckedPackage(args[0], args[1], args[2])}\n`); return; } if (command === "source-digest") { if (!args[0] || args.length !== 1) throw new Error("usage: quixos-qx source-digest ROOT"); const temporary = await mkdtemp(path.join(os.tmpdir(), "qx-source-digest-")); - try {process.stdout.write(`${(await snapshotRepository(args[0], temporary)).treeDigest}\n`);} finally {await rm(temporary, {recursive: true, force: true});} + try { + process.stdout.write(`${(await snapshotRepository(args[0], temporary)).treeDigest}\n`); + } finally { + await rm(temporary, { recursive: true, force: true }); + } return; } if (command === "pin-upgrade") { const [workbench, specFile, ...flags] = args; - if (!workbench || !specFile) throw new Error("usage: quixos-qx pin-upgrade WORKBENCH SPEC_JSON [--publish] [--resume UUID]"); - let publish = false, acceptEdits = false, resume: string | undefined; + if (!workbench || !specFile) + throw new Error("usage: quixos-qx pin-upgrade WORKBENCH SPEC_JSON [--publish] [--resume UUID]"); + let publish = false, + acceptEdits = false, + resume: string | undefined; for (let index = 0; index < flags.length; index++) { if (flags[index] === "--publish") publish = true; else if (flags[index] === "--accept-edits") acceptEdits = true; @@ -239,66 +365,112 @@ const main = async () => { else throw new Error(`Unknown pin-upgrade option ${flags[index]}`); } if (acceptEdits && !resume) throw new Error("--accept-edits requires an existing refactor journal (--resume)"); - const plan = resume ? JSON.parse(await readFile(path.join(workbench, ".quixos/upgrades", `${resume}.json`), "utf8")).plan - : await planPinUpgrades(workbench, specFile === "auto" ? await discoverUpgradeSpec(workbench) : JSON.parse(await readFile(specFile, "utf8")) as UpgradeSpec); - if (resume && path.resolve(workbench) !== plan.workbench) throw new Error("Upgrade journal belongs to another workbench"); - process.stdout.write(`${JSON.stringify(publish ? await applyPinUpgrades(plan, resume, undefined, {acceptEdits}) : plan, null, 2)}\n`); + const plan = resume + ? JSON.parse(await readFile(path.join(workbench, ".quixos/upgrades", `${resume}.json`), "utf8")).plan + : await planPinUpgrades( + workbench, + specFile === "auto" + ? await discoverUpgradeSpec(workbench) + : (JSON.parse(await readFile(specFile, "utf8")) as UpgradeSpec), + ); + if (resume && path.resolve(workbench) !== plan.workbench) + throw new Error("Upgrade journal belongs to another workbench"); + process.stdout.write( + `${JSON.stringify(publish ? await applyPinUpgrades(plan, resume, undefined, { acceptEdits }) : plan, null, 2)}\n`, + ); return; } - if (command === "scaffold-refresh") throw new Error("Refresh is no longer required: edit declarations and typed implementation wiring, then run qx-workspace check. Dependency installation uses scaffold install."); + if (command === "scaffold-refresh") + throw new Error( + "Refresh is no longer required: edit declarations and typed implementation wiring, then run qx-workspace check. Dependency installation uses scaffold install.", + ); if (["scaffold-package", "scaffold-function", "scaffold-migration", "scaffold-install"].includes(command)) { const [root, specFile, ...flags] = args; let spec: ScaffoldRecipe; if (command === "scaffold-function" && /^[A-Za-z_][A-Za-z0-9_]*$/.test(specFile ?? "")) { const authored = parseQx(await readFile(path.join(root, "package.qx"), "utf8")); - const declarationNode = [...walkSyntax(authored.root)].find(node => node.kind === "packageResourceDecl"); - const nameNode = declarationNode?.children.find(node => node.kind === "identifier"); + const declarationNode = [...walkSyntax(authored.root)].find((node) => node.kind === "packageResourceDecl"); + const nameNode = declarationNode?.children.find((node) => node.kind === "identifier"); if (!nameNode) throw new Error("Expected a package declaration"); const name = authored.source.slice(nameNode.start, nameNode.end); - spec = {source: await authorSource(root), name: specFile, id: `export:${name}:${specFile}`}; + spec = { source: await authorSource(root), name: specFile, id: `export:${name}:${specFile}` }; const declaration = flags.indexOf("--declaration"); if (declaration >= 0) { if (!flags[declaration + 1]) throw new Error("--declaration requires a QX declaration file"); spec.declaration = await readFile(flags[declaration + 1], "utf8"); const parsed = parseQx(`package Draft id "package:draft" revision "package:draft@1" { ${spec.declaration} }`); - if (parsed.diagnostics.length) throw new Error(parsed.diagnostics.map(d => d.message).join("\n")); - const exported = [...walkSyntax(parsed.root)].filter(node => ["packageOperationExport", "packageFunctionExport", "packageConstructorExport"].includes(node.kind)); - if (exported.length !== 1) throw new Error("--declaration must contain exactly one function, operation or constructor export"); - const literal = exported[0].children.find(node => node.kind === "stringLiteral"); + if (parsed.diagnostics.length) throw new Error(parsed.diagnostics.map((d) => d.message).join("\n")); + const exported = [...walkSyntax(parsed.root)].filter((node) => + ["packageOperationExport", "packageFunctionExport", "packageConstructorExport"].includes(node.kind), + ); + if (exported.length !== 1) + throw new Error("--declaration must contain exactly one function, operation or constructor export"); + const literal = exported[0].children.find((node) => node.kind === "stringLiteral"); if (!literal) throw new Error("Declaration requires an authored export ID"); spec.id = JSON.parse(parsed.source.slice(literal.start, literal.end)); flags.splice(declaration, 2); } - } else spec = await readSpec(specFile) as ScaffoldRecipe; - if (!root || !specFile || flags.some((flag) => !["--write", "--install"].includes(flag)) || (flags.includes("--install") && !flags.includes("--write"))) throw new Error("usage: quixos-qx scaffold-package|function|migration|refresh ROOT SPEC_JSON [--write [--install]]"); - const plan = command === "scaffold-install" ? undefined : await planStructure(root, - await scaffoldRecipe(root, command.slice(9) as "package" | "function" | "migration", spec), process.env.QUIXOS_SNAPSHOT_MAP); + } else spec = (await readSpec(specFile)) as ScaffoldRecipe; + if ( + !root || + !specFile || + flags.some((flag) => !["--write", "--install"].includes(flag)) || + (flags.includes("--install") && !flags.includes("--write")) + ) + throw new Error( + "usage: quixos-qx scaffold-package|function|migration|refresh ROOT SPEC_JSON [--write [--install]]", + ); + const plan = + command === "scaffold-install" + ? undefined + : await planStructure( + root, + await scaffoldRecipe(root, command.slice(9) as "package" | "function" | "migration", spec), + process.env.QUIXOS_SNAPSHOT_MAP, + ); const applied = plan && flags.includes("--write") ? await applyStructure(plan) : undefined; if (flags.includes("--install")) { const cwd = path.resolve(root, spec.directory ?? ""); const toolchain = JSON.parse(await readFile(path.join(cwd, "quixos.toolchain.json"), "utf8")); - if (toolchain.generatedBy !== "qx-scaffold-v1" || typeof toolchain.nixifyPluginUrl !== "string") throw new Error("Missing scaffold toolchain"); - for (const [executable, args] of [["corepack", ["yarn", "plugin", "import", toolchain.nixifyPluginUrl]], ["corepack", ["yarn", "config", "set", "generateDefaultNix", "false"]], ["corepack", ["yarn", "config", "set", "individualNixPackaging", "true"]], ["corepack", ["yarn", "install"]]] as const) { - const result = spawnSync(executable, [...args], {cwd, stdio: ["inherit", 2, 2]}); - if (result.error || result.status !== 0) throw new Error(`Scaffold files retained; ${executable} ${args.join(" ")} failed: ${result.error?.message ?? result.status}`); + if (toolchain.generatedBy !== "qx-scaffold-v1" || typeof toolchain.nixifyPluginUrl !== "string") + throw new Error("Missing scaffold toolchain"); + for (const [executable, args] of [ + ["corepack", ["yarn", "plugin", "import", toolchain.nixifyPluginUrl]], + ["corepack", ["yarn", "config", "set", "generateDefaultNix", "false"]], + ["corepack", ["yarn", "config", "set", "individualNixPackaging", "true"]], + ["corepack", ["yarn", "install"]], + ] as const) { + const result = spawnSync(executable, [...args], { cwd, stdio: ["inherit", 2, 2] }); + if (result.error || result.status !== 0) + throw new Error( + `Scaffold files retained; ${executable} ${args.join(" ")} failed: ${result.error?.message ?? result.status}`, + ); + } + try { + await readFile(path.join(cwd, "yarn-project.nix")); + } catch { + throw new Error( + "Nixify did not generate yarn-project.nix. It skips repositories under the OS temporary directory; use an ordinary workspace checkout and retry installation.", + ); } - try {await readFile(path.join(cwd, "yarn-project.nix"));} - catch {throw new Error("Nixify did not generate yarn-project.nix. It skips repositories under the OS temporary directory; use an ordinary workspace checkout and retry installation.");} await snapshotCommit(cwd); - const locked = spawnSync("nix", ["flake", "lock"], {cwd, stdio: ["inherit", 2, 2]}); + const locked = spawnSync("nix", ["flake", "lock"], { cwd, stdio: ["inherit", 2, 2] }); if (locked.error || locked.status !== 0) throw new Error("Scaffold files retained; nix flake lock failed"); } - process.stdout.write(`${JSON.stringify({...plan ? planSummary(plan) : {installed: true}, applied}, null, 2)}\n`); + process.stdout.write( + `${JSON.stringify({ ...(plan ? planSummary(plan) : { installed: true }), applied }, null, 2)}\n`, + ); return; } if (command === "scaffold-structure") { const [root, spec, ...flags] = args; - if (!root || !spec || flags.some((flag) => flag !== "--write")) throw new Error("usage: quixos-qx scaffold-structure ROOT SPEC_JSON [--write]"); - const request = await readSpec(spec) as StructuralRequest; + if (!root || !spec || flags.some((flag) => flag !== "--write")) + throw new Error("usage: quixos-qx scaffold-structure ROOT SPEC_JSON [--write]"); + const request = (await readSpec(spec)) as StructuralRequest; if (request.kind !== "workspace" && !request.source) request.source = await authorSource(root); const plan = await planStructure(root, request, process.env.QUIXOS_SNAPSHOT_MAP); const applied = flags.includes("--write") ? await applyStructure(plan) : undefined; - process.stdout.write(`${JSON.stringify({...planSummary(plan), applied}, null, 2)}\n`); + process.stdout.write(`${JSON.stringify({ ...planSummary(plan), applied }, null, 2)}\n`); return; } if (command === "scaffold-resume") { @@ -307,10 +479,14 @@ const main = async () => { process.stdout.write(`${JSON.stringify(await resumeStructure(root, id), null, 2)}\n`); return; } - if (["check", "check-resource"].includes(command)) throw new Error("Use qx-workspace check in the registered repository; handwritten source/snapshot-map candidates are no longer an authoring check path"); + if (["check", "check-resource"].includes(command)) + throw new Error( + "Use qx-workspace check in the registered repository; handwritten source/snapshot-map candidates are no longer an authoring check path", + ); if (command === "evolution") { const [baseline, candidate, reviews, ...extra] = args; - if (!baseline || !candidate || extra.length) throw new Error("usage: quixos-qx evolution BASELINE_JSON CANDIDATE_JSON [REVIEWS_JSON]"); + if (!baseline || !candidate || extra.length) + throw new Error("usage: quixos-qx evolution BASELINE_JSON CANDIDATE_JSON [REVIEWS_JSON]"); const before = baseline === "none" ? null : JSON.parse(await readFile(baseline, "utf8")); const after = JSON.parse(await readFile(candidate, "utf8")); const decisions = reviews ? JSON.parse(await readFile(reviews, "utf8")) : []; @@ -320,23 +496,35 @@ const main = async () => { } if (command === "scaffold-atom") { const [root, name, id, ...flags] = args; - if (!root || !name || !id || flags.some((flag) => flag !== "--write")) throw new Error("usage: quixos-qx scaffold-atom ROOT NAME ID [--write]"); + if (!root || !name || !id || flags.some((flag) => flag !== "--write")) + throw new Error("usage: quixos-qx scaffold-atom ROOT NAME ID [--write]"); const resolveResource = await createGitCapabilityResolver({ checkoutRoot: `${root}/.quixos/resource-checkouts` }); const plan = await scaffoldAtom({ root, name, id, write: flags.includes("--write"), resolveResource }); process.stdout.write(`${JSON.stringify(plan, null, 2)}\n`); return; } const [file, flag, ...rest] = args; - if (!file || rest.length || (flag && flag !== "--write") || !["parse", "lint", "format"].includes(command ?? "") || - (flag && command !== "format")) throw new Error("usage: quixos-qx parse|lint|format FILE [--write (format only)]"); + if ( + !file || + rest.length || + (flag && flag !== "--write") || + !["parse", "lint", "format"].includes(command ?? "") || + (flag && command !== "format") + ) + throw new Error("usage: quixos-qx parse|lint|format FILE [--write (format only)]"); const source = await readFile(file, "utf8"); if (command === "format") { const formatted = formatQx(source); - if (flag) await writeFile(file, formatted); else process.stdout.write(formatted); + if (flag) await writeFile(file, formatted); + else process.stdout.write(formatted); } else { const result = command === "parse" ? parseQx(source, file) : lintQx(source, file); process.stdout.write(`${JSON.stringify(result, null, 2)}\n`); - if (Array.isArray(result) ? result.some((entry) => entry.severity === "error") : result.diagnostics.length) process.exitCode = 1; + if (Array.isArray(result) ? result.some((entry) => entry.severity === "error") : result.diagnostics.length) + process.exitCode = 1; } }; -main().catch((error: unknown) => { console.error(error instanceof Error ? error.message : error); process.exitCode = 1; }); +main().catch((error: unknown) => { + console.error(error instanceof Error ? error.message : error); + process.exitCode = 1; +}); diff --git a/src/capability-language/workspace-cli.ts b/src/capability-language/workspace-cli.ts index 75f69e7..bf870b8 100644 --- a/src/capability-language/workspace-cli.ts +++ b/src/capability-language/workspace-cli.ts @@ -4,7 +4,12 @@ import { readFile, writeFile } from "node:fs/promises"; import process from "node:process"; import { compileWorkspaceRepository } from "./assembly.js"; import { createGitCapabilityResolver } from "./git-resolver.js"; -import { planEvolution, runtimeContracts, type EvolutionReview, type WorkspaceRevision } from "../capability-model/index.js"; +import { + planEvolution, + runtimeContracts, + type EvolutionReview, + type WorkspaceRevision, +} from "../capability-model/index.js"; const usage = `usage: quixos-workspace-compile --root DIRECTORY --checkout-root DIRECTORY [--snapshot-map PATH] [--graph-out PATH] [--workspace-id ID] [--workspace-revision-id ID] @@ -59,40 +64,53 @@ const main = async () => { }); if (options.graphOut) { - await writeFile(options.graphOut, `${JSON.stringify({ - formatVersion: 1, - quixos: assembled.lock.quixos, - directResources: [...assembled.directResources.entries()].map(([bindingKey, node]) => { - const [kind, binding] = bindingKey.split("\0"); - return { kind, binding, resourceKey: node.key, directory: node.directory }; - }), - resources: assembled.resources.map((node) => ({ - key: node.key, - kind: node.kind, - source: node.source, - directory: node.directory, - resourceId: node.resource.kind === "interface" - ? node.resource.revision.interfaceId - : node.resource.revision.packageId, - revisionId: node.resource.revision.revisionId, - dependencies: [...node.dependencies.entries()].map(([binding, dependency]) => ({ - binding, - resourceKey: dependency.key, - })), - })), - }, null, 2)}\n`); + await writeFile( + options.graphOut, + `${JSON.stringify( + { + formatVersion: 1, + quixos: assembled.lock.quixos, + directResources: [...assembled.directResources.entries()].map(([bindingKey, node]) => { + const [kind, binding] = bindingKey.split("\0"); + return { kind, binding, resourceKey: node.key, directory: node.directory }; + }), + resources: assembled.resources.map((node) => ({ + key: node.key, + kind: node.kind, + source: node.source, + directory: node.directory, + resourceId: + node.resource.kind === "interface" + ? node.resource.revision.interfaceId + : node.resource.revision.packageId, + revisionId: node.resource.revision.revisionId, + dependencies: [...node.dependencies.entries()].map(([binding, dependency]) => ({ + binding, + resourceKey: dependency.key, + })), + })), + }, + null, + 2, + )}\n`, + ); } const candidate = { ...assembled.workspace, executionContracts: runtimeContracts(assembled.workspace) }; if (options.evolutionOut) { - const baseline = options.baseline ? JSON.parse(await readFile(options.baseline, "utf8")) as WorkspaceRevision : null; - const reviews = options.reviews ? JSON.parse(await readFile(options.reviews, "utf8")) as EvolutionReview[] : []; + const baseline = options.baseline + ? (JSON.parse(await readFile(options.baseline, "utf8")) as WorkspaceRevision) + : null; + const reviews = options.reviews ? (JSON.parse(await readFile(options.reviews, "utf8")) as EvolutionReview[]) : []; if (!Array.isArray(reviews)) throw new Error("Review file must contain an array"); - await writeFile(options.evolutionOut, `${JSON.stringify(planEvolution(baseline, candidate, { reviews }), null, 2)}\n`); + await writeFile( + options.evolutionOut, + `${JSON.stringify(planEvolution(baseline, candidate, { reviews }), null, 2)}\n`, + ); } process.stdout.write(`${JSON.stringify(candidate, null, 2)}\n`); }; main().catch((error: unknown) => { - process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}\n`); + process.stderr.write(`${error instanceof Error ? (error.stack ?? error.message) : String(error)}\n`); process.exitCode = 1; }); diff --git a/src/capability-model/evolution.ts b/src/capability-model/evolution.ts index 6014f9a..31873f8 100644 --- a/src/capability-model/evolution.ts +++ b/src/capability-model/evolution.ts @@ -3,50 +3,75 @@ import type { Binding, Conformance, DependencyBinding, PersistentAttachment, Wor import { validateWorkspaceRevision } from "./validation.js"; /** Content hashing is independent of JSON object insertion order, not array order. */ -const compareText = (a: string, b: string) => a < b ? -1 : a > b ? 1 : 0; +const compareText = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0); export const canonicalJson = (value: unknown): string => { if (value === null || typeof value === "string" || typeof value === "boolean") return JSON.stringify(value); if (typeof value === "number" && Number.isFinite(value)) return JSON.stringify(value); if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`; if (typeof value === "object" && value !== null) { - if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) throw new Error("Expected a plain JSON object"); - return `{${Object.entries(value).filter(([, entry]) => entry !== undefined).sort(([a], [b]) => compareText(a, b)) - .map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`).join(",")}}`; + if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) + throw new Error("Expected a plain JSON object"); + return `{${Object.entries(value) + .filter(([, entry]) => entry !== undefined) + .sort(([a], [b]) => compareText(a, b)) + .map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`) + .join(",")}}`; } throw new Error(`Cannot hash non-JSON value: ${typeof value}`); }; -export const contentDigest = (value: unknown) => `sha256:${createHash("sha256").update(canonicalJson(value)).digest("hex")}`; +export const contentDigest = (value: unknown) => + `sha256:${createHash("sha256").update(canonicalJson(value)).digest("hex")}`; const semantic = (value: unknown): unknown => { if (Array.isArray(value)) return value.map(semantic); - if (value && typeof value === "object") return Object.fromEntries(Object.entries(value) - .filter(([key, entry]) => entry !== undefined && key !== "displayName" && key !== "documentation") - // These are authored data/maps, not schema nodes. A user field literally - // named displayName or documentation is semantic and must stay in the hash. - .map(([key, entry]) => [key, key === "defaultValue" || key === "fields" ? entry : semantic(entry)])); + if (value && typeof value === "object") + return Object.fromEntries( + Object.entries(value) + .filter(([key, entry]) => entry !== undefined && key !== "displayName" && key !== "documentation") + // These are authored data/maps, not schema nodes. A user field literally + // named displayName or documentation is semantic and must stay in the hash. + .map(([key, entry]) => [key, key === "defaultValue" || key === "fields" ? entry : semantic(entry)]), + ); return value; }; -const sorted = (entries: readonly T[], key: (entry: T) => string) => [...entries].sort((a, b) => compareText(key(a), key(b))); -export const conformanceIdentity = (entry: Conformance): string => entry.id ?? `legacy:${entry.atomId}:${entry.interfaceRevisionId}`; +const sorted = (entries: readonly T[], key: (entry: T) => string) => + [...entries].sort((a, b) => compareText(key(a), key(b))); +export const conformanceIdentity = (entry: Conformance): string => + entry.id ?? `legacy:${entry.atomId}:${entry.interfaceRevisionId}`; -export type StorageContract = { id: string; ownerId: string; kind: "state" | "edge"; digest: string; definition: unknown }; +export type StorageContract = { + id: string; + ownerId: string; + kind: "state" | "edge"; + digest: string; + definition: unknown; +}; /** Automatic evolution preserves values; it never interprets migration code or * guesses that a new nominal message descriptor means the same representation. */ -export const storageChangeRequiresMigration = (previous: StorageContract | undefined, next: StorageContract | undefined, - oldAtomIds: ReadonlySet): boolean => { +export const storageChangeRequiresMigration = ( + previous: StorageContract | undefined, + next: StorageContract | undefined, + oldAtomIds: ReadonlySet, +): boolean => { if (!next) return true; const after = next.definition as PersistentAttachment; if (!previous) { - if (after.kind === "state") return oldAtomIds.has(after.attachedTo) && after.defaultValue === undefined && after.valueType.kind !== "optional"; - return after.endpoints.some(endpoint => endpoint.cardinality === "exactly-one" && - (endpoint.constraint.kind !== "atom" || oldAtomIds.has(endpoint.constraint.atomId))); + if (after.kind === "state") + return ( + oldAtomIds.has(after.attachedTo) && after.defaultValue === undefined && after.valueType.kind !== "optional" + ); + return after.endpoints.some( + (endpoint) => + endpoint.cardinality === "exactly-one" && + (endpoint.constraint.kind !== "atom" || oldAtomIds.has(endpoint.constraint.atomId)), + ); } if (previous.ownerId !== next.ownerId || previous.kind !== next.kind) return true; const before = previous.definition as PersistentAttachment; if (before.kind === "state" && after.kind === "state") { // Capture materializes old defaults, so changing a default affects only // newly constructed objects, not existing sparse state. - const {defaultValue: _beforeDefault, ...beforeStorage} = before; - const {defaultValue: _afterDefault, ...afterStorage} = after; + const { defaultValue: _beforeDefault, ...beforeStorage } = before; + const { defaultValue: _afterDefault, ...afterStorage } = after; return canonicalJson(beforeStorage) !== canonicalJson(afterStorage); } return canonicalJson(before) !== canonicalJson(after); @@ -55,15 +80,29 @@ export const storageContracts = (workspace: WorkspaceRevision): StorageContract[ const result: StorageContract[] = []; const add = (attachment: PersistentAttachment, ownerId: string) => { const definition = semantic(attachment); - result.push({ id: attachment.id, ownerId, kind: attachment.kind, definition, digest: contentDigest({ ownerId, definition }) }); + result.push({ + id: attachment.id, + ownerId, + kind: attachment.kind, + definition, + digest: contentDigest({ ownerId, definition }), + }); }; for (const attachment of workspace.sharedAttachments) add(attachment, "legacy:workspace"); - for (const conformance of workspace.conformances) for (const attachment of conformance.privateAttachments) add(attachment, conformanceIdentity(conformance)); + for (const conformance of workspace.conformances) + for (const attachment of conformance.privateAttachments) add(attachment, conformanceIdentity(conformance)); return sorted(result, (entry) => entry.id); }; type GraphNode = { value: unknown; dependencies: Set; reviewProviders: Set }; -export type RuntimeContract = { groupId: string; packageId: string; packageRevisionId: string; digest: string; reviewProviders: string[]; dependencies: Array<{ id: string; digest: string }> }; +export type RuntimeContract = { + groupId: string; + packageId: string; + packageRevisionId: string; + digest: string; + reviewProviders: string[]; + dependencies: Array<{ id: string; digest: string }>; +}; /** Build only outbound execution dependencies. Incoming callers never retain or invalidate a provider. */ export const runtimeContracts = (workspace: WorkspaceRevision): RuntimeContract[] => { @@ -75,13 +114,23 @@ export const runtimeContracts = (workspace: WorkspaceRevision): RuntimeContract[ }; const conformanceKey = (atom: string, iface: string) => `conformance:${atom}:${iface}`; for (const storage of storageContracts(workspace)) node(`attachment:${storage.id}`, storage); - for (const iface of workspace.interfaceImports) node(`interface:${iface.revisionId}`, { - ...iface, members: sorted(iface.members, (entry) => entry.id).map((entry) => ({ ...entry, operations: sorted(entry.operations, (operation) => operation.id) })), - }); - for (const pkg of workspace.packageImports) node(`package:${pkg.revisionId}`, { - ...pkg, semanticMajor: pkg.semanticMajor ?? 1, - exports: sorted(pkg.exports, (entry) => entry.id).map((entry) => ({ ...entry, dependencyPorts: sorted(entry.dependencyPorts, (port) => port.id) })), - }); + for (const iface of workspace.interfaceImports) + node(`interface:${iface.revisionId}`, { + ...iface, + members: sorted(iface.members, (entry) => entry.id).map((entry) => ({ + ...entry, + operations: sorted(entry.operations, (operation) => operation.id), + })), + }); + for (const pkg of workspace.packageImports) + node(`package:${pkg.revisionId}`, { + ...pkg, + semanticMajor: pkg.semanticMajor ?? 1, + exports: sorted(pkg.exports, (entry) => entry.id).map((entry) => ({ + ...entry, + dependencyPorts: sorted(entry.dependencyPorts, (port) => port.id), + })), + }); const dependency = (parent: GraphNode, binding: DependencyBinding, atomId: string, reviews?: Set) => { if (binding.kind === "state") parent.dependencies.add(`attachment:${binding.slotId}`); if (binding.kind === "edge") parent.dependencies.add(`attachment:${binding.edgeTypeId}`); @@ -96,7 +145,10 @@ export const runtimeContracts = (workspace: WorkspaceRevision): RuntimeContract[ parent.dependencies.add(`interface:${binding.interfaceRevisionId}`); // An edge traversal may select any matching target. Conservatively include every possible witness. for (const conformance of workspace.conformances) { - if (conformance.interfaceRevisionId === binding.interfaceRevisionId && (binding.via || conformance.atomId === atomId)) { + if ( + conformance.interfaceRevisionId === binding.interfaceRevisionId && + (binding.via || conformance.atomId === atomId) + ) { parent.dependencies.add(conformanceKey(conformance.atomId, conformance.interfaceRevisionId)); reviews?.add(conformanceIdentity(conformance)); for (const operation of conformance.operationBindings) { @@ -110,7 +162,10 @@ export const runtimeContracts = (workspace: WorkspaceRevision): RuntimeContract[ } }; const binding = (parent: GraphNode, value: Binding, atomId: string, context: unknown) => { - if (value.kind !== "package") { dependency(parent, value, atomId); return; } + if (value.kind !== "package") { + dependency(parent, value, atomId); + return; + } const packageNode = nodes.get(`package:${value.packageRevisionId}`)!; parent.dependencies.add(`package:${value.packageRevisionId}`); const normalized = { ...value, dependencies: sorted(value.dependencies, (entry) => entry.portId) }; @@ -121,16 +176,21 @@ export const runtimeContracts = (workspace: WorkspaceRevision): RuntimeContract[ }; for (const conformance of workspace.conformances) { const parent = node(conformanceKey(conformance.atomId, conformance.interfaceRevisionId), { - id: conformanceIdentity(conformance), semanticMajor: conformance.semanticMajor ?? 1, - atomId: conformance.atomId, interfaceRevisionId: conformance.interfaceRevisionId, + id: conformanceIdentity(conformance), + semanticMajor: conformance.semanticMajor ?? 1, + atomId: conformance.atomId, + interfaceRevisionId: conformance.interfaceRevisionId, operations: sorted(conformance.operationBindings, (entry) => entry.operationId), materializations: sorted(conformance.relationshipMaterializations, (entry) => entry.memberId), }); parent.dependencies.add(`interface:${conformance.interfaceRevisionId}`); for (const attachment of conformance.privateAttachments) parent.dependencies.add(`attachment:${attachment.id}`); - for (const operation of conformance.operationBindings) binding(parent, operation.binding, conformance.atomId, { - conformanceId: conformanceIdentity(conformance), semanticMajor: conformance.semanticMajor ?? 1, operationId: operation.operationId, - }); + for (const operation of conformance.operationBindings) + binding(parent, operation.binding, conformance.atomId, { + conformanceId: conformanceIdentity(conformance), + semanticMajor: conformance.semanticMajor ?? 1, + operationId: operation.operationId, + }); for (const materialization of conformance.relationshipMaterializations) { parent.dependencies.add(`constructor:${materialization.constructorAtomId}`); parent.dependencies.add(`attachment:${materialization.edgeTypeId}`); @@ -142,91 +202,195 @@ export const runtimeContracts = (workspace: WorkspaceRevision): RuntimeContract[ } const counts = new Map(); for (const pkg of workspace.packageImports) counts.set(pkg.packageId, (counts.get(pkg.packageId) ?? 0) + 1); - return sorted(workspace.packageImports.map((pkg): RuntimeContract => { - const visited = new Set(); - const walk = (key: string) => { - if (visited.has(key)) return; - const entry = nodes.get(key); - if (!entry) throw new Error(`Unresolved execution dependency ${key}`); - visited.add(key); - for (const target of entry.dependencies) walk(target); - }; - walk(`package:${pkg.revisionId}`); - const dependencies = [...visited].sort().map((id) => ({ id, digest: contentDigest(nodes.get(id)!.value) })); - return { groupId: counts.get(pkg.packageId) === 1 ? pkg.packageId : `${pkg.packageId}#${pkg.revisionId}`, - packageId: pkg.packageId, packageRevisionId: pkg.revisionId, digest: contentDigest(dependencies), - reviewProviders: [...nodes.get(`package:${pkg.revisionId}`)!.reviewProviders].sort(), dependencies }; - }), (entry) => entry.groupId); + return sorted( + workspace.packageImports.map((pkg): RuntimeContract => { + const visited = new Set(); + const walk = (key: string) => { + if (visited.has(key)) return; + const entry = nodes.get(key); + if (!entry) throw new Error(`Unresolved execution dependency ${key}`); + visited.add(key); + for (const target of entry.dependencies) walk(target); + }; + walk(`package:${pkg.revisionId}`); + const dependencies = [...visited].sort().map((id) => ({ id, digest: contentDigest(nodes.get(id)!.value) })); + return { + groupId: counts.get(pkg.packageId) === 1 ? pkg.packageId : `${pkg.packageId}#${pkg.revisionId}`, + packageId: pkg.packageId, + packageRevisionId: pkg.revisionId, + digest: contentDigest(dependencies), + reviewProviders: [...nodes.get(`package:${pkg.revisionId}`)!.reviewProviders].sort(), + dependencies, + }; + }), + (entry) => entry.groupId, + ); }; -export type EvolutionReview = { requirementDigest: string; decision: "changed" | "accepted-unchanged"; rationale: string; agentId: string }; -export type ReviewRequirement = { consumerId: string; providerId: string; oldMajor: number; newMajor: number; requirementDigest: string }; -export type RuntimeAction = { groupId: string; action: "keep" | "start" | "replace" | "retire"; previous?: RuntimeContract; candidate?: RuntimeContract; reasons: string[] }; +export type EvolutionReview = { + requirementDigest: string; + decision: "changed" | "accepted-unchanged"; + rationale: string; + agentId: string; +}; +export type ReviewRequirement = { + consumerId: string; + providerId: string; + oldMajor: number; + newMajor: number; + requirementDigest: string; +}; +export type RuntimeAction = { + groupId: string; + action: "keep" | "start" | "replace" | "retire"; + previous?: RuntimeContract; + candidate?: RuntimeContract; + reasons: string[]; +}; export type EvolutionReport = { - schemaVersion: 1; baselineDigest: string | null; candidateDigest: string; checkerVersion: string; + schemaVersion: 1; + baselineDigest: string | null; + candidateDigest: string; + checkerVersion: string; runtimeActions: RuntimeAction[]; - storageChanges: Array<{ id: string; kind: "add" | "remove" | "change"; requiresMigration: boolean; previous?: StorageContract; candidate?: StorageContract }>; + storageChanges: Array<{ + id: string; + kind: "add" | "remove" | "change"; + requiresMigration: boolean; + previous?: StorageContract; + candidate?: StorageContract; + }>; migrationRequired: string[]; reviews: Array; packageChecks: Array<{ groupId: string; contractDigest: string }>; blockers: string[]; }; -export const planEvolution = (baseline: WorkspaceRevision | null, candidate: WorkspaceRevision, - options: { reviews?: EvolutionReview[]; allowLegacy?: boolean } = {}): EvolutionReport => { +export const planEvolution = ( + baseline: WorkspaceRevision | null, + candidate: WorkspaceRevision, + options: { reviews?: EvolutionReview[]; allowLegacy?: boolean } = {}, +): EvolutionReport => { const issues = validateWorkspaceRevision(candidate); - if (issues.length) throw new Error(`Invalid candidate workspace:\n${issues.map((entry) => `${entry.path}: ${entry.message}`).join("\n")}`); - if (baseline && baseline.workspaceId !== candidate.workspaceId) throw new Error("Cannot evolve a different workspace"); + if (issues.length) + throw new Error( + `Invalid candidate workspace:\n${issues.map((entry) => `${entry.path}: ${entry.message}`).join("\n")}`, + ); + if (baseline && baseline.workspaceId !== candidate.workspaceId) + throw new Error("Cannot evolve a different workspace"); const candidateDigest = contentDigest(candidate); const checkerVersion = "quixos-evolution-v1"; const blockers: string[] = []; if (!options.allowLegacy) { - if (candidate.sharedAttachments.length) blockers.push("Assign legacy workspace-shared attachments to explicit conformance owners"); - for (const entry of candidate.conformances) if (!entry.id) blockers.push(`Conformance ${entry.atomId} as ${entry.interfaceRevisionId} requires an authored ID`); + if (candidate.sharedAttachments.length) + blockers.push("Assign legacy workspace-shared attachments to explicit conformance owners"); + for (const entry of candidate.conformances) + if (!entry.id) + blockers.push(`Conformance ${entry.atomId} as ${entry.interfaceRevisionId} requires an authored ID`); } const previousRuntimes = new Map((baseline ? runtimeContracts(baseline) : []).map((entry) => [entry.groupId, entry])); const nextRuntimes = new Map(runtimeContracts(candidate).map((entry) => [entry.groupId, entry])); - const runtimeActions: RuntimeAction[] = [...new Set([...previousRuntimes.keys(), ...nextRuntimes.keys()])].sort().map((groupId) => { - const previous = previousRuntimes.get(groupId), next = nextRuntimes.get(groupId); - const before = new Map(previous?.dependencies.map((entry) => [entry.id, entry.digest])); - const after = new Map(next?.dependencies.map((entry) => [entry.id, entry.digest])); - const reasons = [...new Set([...before.keys(), ...after.keys()])].sort().filter((id) => before.get(id) !== after.get(id)); - return { groupId, action: !previous ? "start" : !next ? "retire" : previous.digest === next.digest ? "keep" : "replace", - ...(previous ? { previous } : {}), ...(next ? { candidate: next } : {}), reasons }; - }); + const runtimeActions: RuntimeAction[] = [...new Set([...previousRuntimes.keys(), ...nextRuntimes.keys()])] + .sort() + .map((groupId) => { + const previous = previousRuntimes.get(groupId), + next = nextRuntimes.get(groupId); + const before = new Map(previous?.dependencies.map((entry) => [entry.id, entry.digest])); + const after = new Map(next?.dependencies.map((entry) => [entry.id, entry.digest])); + const reasons = [...new Set([...before.keys(), ...after.keys()])] + .sort() + .filter((id) => before.get(id) !== after.get(id)); + return { + groupId, + action: !previous ? "start" : !next ? "retire" : previous.digest === next.digest ? "keep" : "replace", + ...(previous ? { previous } : {}), + ...(next ? { candidate: next } : {}), + reasons, + }; + }); const beforeStorage = new Map((baseline ? storageContracts(baseline) : []).map((entry) => [entry.id, entry])); const afterStorage = new Map(storageContracts(candidate).map((entry) => [entry.id, entry])); const storageChanges: EvolutionReport["storageChanges"] = []; for (const id of [...new Set([...beforeStorage.keys(), ...afterStorage.keys()])].sort()) { - const previous = beforeStorage.get(id), next = afterStorage.get(id); - if (previous?.digest !== next?.digest) storageChanges.push({ id, kind: !previous ? "add" : !next ? "remove" : "change", - requiresMigration: storageChangeRequiresMigration(previous, next, new Set(baseline?.atoms.map(atom => atom.id) ?? [])), - ...(previous ? { previous } : {}), ...(next ? { candidate: next } : {}) }); + const previous = beforeStorage.get(id), + next = afterStorage.get(id); + if (previous?.digest !== next?.digest) + storageChanges.push({ + id, + kind: !previous ? "add" : !next ? "remove" : "change", + requiresMigration: storageChangeRequiresMigration( + previous, + next, + new Set(baseline?.atoms.map((atom) => atom.id) ?? []), + ), + ...(previous ? { previous } : {}), + ...(next ? { candidate: next } : {}), + }); } const providers = (workspace: WorkspaceRevision) => [ - ...workspace.packageImports.map((entry) => ({ id: entry.packageId as string, revision: entry.revisionId as string, major: entry.semanticMajor ?? 1, - node: `package:${entry.revisionId}`, digest: contentDigest(entry) })), - ...workspace.conformances.map((entry) => ({ id: conformanceIdentity(entry), revision: contentDigest(entry), major: entry.semanticMajor ?? 1, - node: `conformance:${entry.atomId}:${entry.interfaceRevisionId}`, digest: contentDigest(entry) })), + ...workspace.packageImports.map((entry) => ({ + id: entry.packageId as string, + revision: entry.revisionId as string, + major: entry.semanticMajor ?? 1, + node: `package:${entry.revisionId}`, + digest: contentDigest(entry), + })), + ...workspace.conformances.map((entry) => ({ + id: conformanceIdentity(entry), + revision: contentDigest(entry), + major: entry.semanticMajor ?? 1, + node: `conformance:${entry.atomId}:${entry.interfaceRevisionId}`, + digest: contentDigest(entry), + })), ]; const oldProviders = baseline ? providers(baseline) : []; const reviews: EvolutionReport["reviews"] = []; for (const provider of providers(candidate)) { const old = oldProviders.filter((entry) => entry.id === provider.id); - if (old.length > 1) { blockers.push(`Ambiguous semantic-major lineage for ${provider.id}`); continue; } + if (old.length > 1) { + blockers.push(`Ambiguous semantic-major lineage for ${provider.id}`); + continue; + } if (!old[0] || old[0].major === provider.major) continue; if (provider.major < old[0].major) blockers.push(`Semantic major decreases for ${provider.id}`); for (const consumer of nextRuntimes.values()) { if (consumer.packageId === provider.id || !consumer.reviewProviders.includes(provider.id)) continue; - const requirement = { consumerId: consumer.groupId, providerId: provider.id, oldMajor: old[0].major, newMajor: provider.major }; - const requirementDigest = contentDigest({ ...requirement, oldProvider: old[0].digest, newProvider: provider.digest, consumer: consumer.digest, checkerVersion }); - const accepted = (options.reviews ?? []).some((entry) => entry.requirementDigest === requirementDigest && - ["changed", "accepted-unchanged"].includes(entry.decision) && entry.rationale.trim() && entry.agentId.trim()); + const requirement = { + consumerId: consumer.groupId, + providerId: provider.id, + oldMajor: old[0].major, + newMajor: provider.major, + }; + const requirementDigest = contentDigest({ + ...requirement, + oldProvider: old[0].digest, + newProvider: provider.digest, + consumer: consumer.digest, + checkerVersion, + }); + const accepted = (options.reviews ?? []).some( + (entry) => + entry.requirementDigest === requirementDigest && + ["changed", "accepted-unchanged"].includes(entry.decision) && + entry.rationale.trim() && + entry.agentId.trim(), + ); reviews.push({ ...requirement, requirementDigest, accepted }); if (!accepted) blockers.push(`Semantic-major review required: ${consumer.groupId} consumes ${provider.id}`); } } - return { schemaVersion: 1, baselineDigest: baseline ? contentDigest(baseline) : null, candidateDigest, checkerVersion, - runtimeActions, storageChanges, migrationRequired: storageChanges.filter(entry => entry.requiresMigration).map(entry => entry.id), reviews, packageChecks: runtimeActions.filter((entry) => entry.candidate && entry.action !== "keep") - .map((entry) => ({ groupId: entry.groupId, contractDigest: entry.candidate!.digest })), blockers }; + return { + schemaVersion: 1, + baselineDigest: baseline ? contentDigest(baseline) : null, + candidateDigest, + checkerVersion, + runtimeActions, + storageChanges, + migrationRequired: storageChanges.filter((entry) => entry.requiresMigration).map((entry) => entry.id), + reviews, + packageChecks: runtimeActions + .filter((entry) => entry.candidate && entry.action !== "keep") + .map((entry) => ({ groupId: entry.groupId, contractDigest: entry.candidate!.digest })), + blockers, + }; }; diff --git a/src/capability-model/migrations.ts b/src/capability-model/migrations.ts index 7330f90..c4a367c 100644 --- a/src/capability-model/migrations.ts +++ b/src/capability-model/migrations.ts @@ -4,12 +4,23 @@ import type { PersistentAttachment } from "./types.js"; /** Portable storage shape: local owner/slot/projection identities are supplied * by the consuming workspace's explicit bindings, never baked into this hash. */ export const migrationPortContract = (attachment: PersistentAttachment): unknown => { - if (attachment.kind === "state") return {kind: "state", valueType: attachment.valueType, storagePolicy: attachment.storagePolicy, - ...(attachment.defaultValue === undefined ? {} : {defaultValue: attachment.defaultValue})}; - const endpoint = (value: typeof attachment.endpoints[number]) => ({constraint: value.constraint, cardinality: value.cardinality, - ordered: value.ordered, onDelete: value.onDelete ?? "restrict", retainOther: value.retainOther ?? false, - ...(value.keyType ? {keyType: value.keyType} : {}), ...(value.publicTraversal ? {publicTraversal: true} : {})}); - return {kind: "edge", first: endpoint(attachment.endpoints[0]), second: endpoint(attachment.endpoints[1])}; + if (attachment.kind === "state") + return { + kind: "state", + valueType: attachment.valueType, + storagePolicy: attachment.storagePolicy, + ...(attachment.defaultValue === undefined ? {} : { defaultValue: attachment.defaultValue }), + }; + const endpoint = (value: (typeof attachment.endpoints)[number]) => ({ + constraint: value.constraint, + cardinality: value.cardinality, + ordered: value.ordered, + onDelete: value.onDelete ?? "restrict", + retainOther: value.retainOther ?? false, + ...(value.keyType ? { keyType: value.keyType } : {}), + ...(value.publicTraversal ? { publicTraversal: true } : {}), + }); + return { kind: "edge", first: endpoint(attachment.endpoints[0]), second: endpoint(attachment.endpoints[1]) }; }; export type MigrationDeclaration = { @@ -19,7 +30,12 @@ export type MigrationDeclaration = { to: string; implementation: { exportId: string; file: string; digest: string }; predecessors: string[]; - ports: { name: string; view: "old" | "new"; access: ("read" | "write" | "create" | "edge")[]; contractDigest: string }[]; + ports: { + name: string; + view: "old" | "new"; + access: ("read" | "write" | "create" | "edge")[]; + contractDigest: string; + }[]; preservesOldReaders?: boolean; preservesOldWriters?: boolean; }; @@ -29,56 +45,99 @@ export type MigrationCatalog = { migrations: MigrationDeclaration[]; }; export const validateMigrationCatalog = (value: unknown, exportIds?: ReadonlySet): MigrationCatalog => { - if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Migration catalog must be an object"); + if (!value || typeof value !== "object" || Array.isArray(value)) + throw new Error("Migration catalog must be an object"); const catalog = value as MigrationCatalog; - if (catalog.schemaVersion !== 1 || !catalog.contracts || Array.isArray(catalog.contracts) || !Array.isArray(catalog.migrations)) throw new Error("Unsupported migration catalog"); - for (const [digest, contract] of Object.entries(catalog.contracts)) if (contentDigest(contract) !== digest) throw new Error(`Migration contract digest mismatch: ${digest}`); + if ( + catalog.schemaVersion !== 1 || + !catalog.contracts || + Array.isArray(catalog.contracts) || + !Array.isArray(catalog.migrations) + ) + throw new Error("Unsupported migration catalog"); + for (const [digest, contract] of Object.entries(catalog.contracts)) + if (contentDigest(contract) !== digest) throw new Error(`Migration contract digest mismatch: ${digest}`); const ids = new Set(); for (const migration of catalog.migrations) { - if (!migration.id || !migration.scopeId || ids.has(migration.id)) throw new Error("Migration IDs must be stable and unique"); + if (!migration.id || !migration.scopeId || ids.has(migration.id)) + throw new Error("Migration IDs must be stable and unique"); ids.add(migration.id); - if (!catalog.contracts[migration.from] || !catalog.contracts[migration.to] || migration.from === migration.to) throw new Error(`Migration ${migration.id} requires distinct retained source and target contracts`); - if (!migration.implementation?.exportId || !/^sha256:[0-9a-f]{64}$/.test(migration.implementation.digest)) throw new Error(`Migration ${migration.id} requires an exact implementation digest`); - if (!migration.implementation.file || migration.implementation.file.startsWith("/") || migration.implementation.file.split(/[\\/]/).some((part) => !part || part === "." || part === "..")) throw new Error("Migration implementation must be a relative package file"); - if (exportIds && !exportIds.has(migration.implementation.exportId)) throw new Error(`Migration ${migration.id} refers to an undeclared package export`); - if (!Array.isArray(migration.predecessors) || !Array.isArray(migration.ports)) throw new Error(`Migration ${migration.id} requires predecessors and ports`); - if (new Set(migration.predecessors).size !== migration.predecessors.length) throw new Error(`Duplicate predecessor in ${migration.id}`); - for (const promise of [migration.preservesOldReaders, migration.preservesOldWriters]) if (promise !== undefined && typeof promise !== "boolean") throw new Error("Migration compatibility promises must be booleans"); + if (!catalog.contracts[migration.from] || !catalog.contracts[migration.to] || migration.from === migration.to) + throw new Error(`Migration ${migration.id} requires distinct retained source and target contracts`); + if (!migration.implementation?.exportId || !/^sha256:[0-9a-f]{64}$/.test(migration.implementation.digest)) + throw new Error(`Migration ${migration.id} requires an exact implementation digest`); + if ( + !migration.implementation.file || + migration.implementation.file.startsWith("/") || + migration.implementation.file.split(/[\\/]/).some((part) => !part || part === "." || part === "..") + ) + throw new Error("Migration implementation must be a relative package file"); + if (exportIds && !exportIds.has(migration.implementation.exportId)) + throw new Error(`Migration ${migration.id} refers to an undeclared package export`); + if (!Array.isArray(migration.predecessors) || !Array.isArray(migration.ports)) + throw new Error(`Migration ${migration.id} requires predecessors and ports`); + if (new Set(migration.predecessors).size !== migration.predecessors.length) + throw new Error(`Duplicate predecessor in ${migration.id}`); + for (const promise of [migration.preservesOldReaders, migration.preservesOldWriters]) + if (promise !== undefined && typeof promise !== "boolean") + throw new Error("Migration compatibility promises must be booleans"); const ports = new Set(); for (const port of migration.ports) { - if (!port.name || ports.has(port.name) || !["old", "new"].includes(port.view) || !Array.isArray(port.access) || !port.access.length - || port.access.some((access) => !["read", "write", "create", "edge"].includes(access)) || !catalog.contracts[port.contractDigest]) throw new Error(`Invalid migration port in ${migration.id}`); - if (port.view === "old" && port.access.some((access) => access !== "read")) throw new Error("Old migration views are read-only"); + if ( + !port.name || + ports.has(port.name) || + !["old", "new"].includes(port.view) || + !Array.isArray(port.access) || + !port.access.length || + port.access.some((access) => !["read", "write", "create", "edge"].includes(access)) || + !catalog.contracts[port.contractDigest] + ) + throw new Error(`Invalid migration port in ${migration.id}`); + if (port.view === "old" && port.access.some((access) => access !== "read")) + throw new Error("Old migration views are read-only"); ports.add(port.name); } } - for (const migration of catalog.migrations) for (const predecessor of migration.predecessors) if (!ids.has(predecessor)) throw new Error(`Missing retained predecessor ${predecessor}`); + for (const migration of catalog.migrations) + for (const predecessor of migration.predecessors) + if (!ids.has(predecessor)) throw new Error(`Missing retained predecessor ${predecessor}`); // Catalogs are retained across releases. Reject impossible histories at // publication/check time, not only when someone tries to select a path. const remaining = new Map(catalog.migrations.map((entry) => [entry.id, new Set(entry.predecessors)])); const ready = [...remaining].filter(([, dependencies]) => dependencies.size === 0).map(([id]) => id); for (let index = 0; index < ready.length; index++) { remaining.delete(ready[index]); - for (const [id, dependencies] of remaining) if (dependencies.delete(ready[index]) && dependencies.size === 0) ready.push(id); + for (const [id, dependencies] of remaining) + if (dependencies.delete(ready[index]) && dependencies.size === 0) ready.push(id); } if (remaining.size) throw new Error(`Cyclic migration predecessors: ${[...remaining.keys()].join(", ")}`); return catalog; }; export type MigrationSelection = { - scopeId: string; from: string; to: string; path: string[]; + scopeId: string; + from: string; + to: string; + path: string[]; bindings: Record; }; /** Explicit paths, not shortest-path guesses. Receipts identify code plus local scope mapping. */ -export const selectMigrationPath = (catalog: MigrationCatalog, selection: MigrationSelection, previousReceipts: ReadonlyMap = new Map()) => { +export const selectMigrationPath = ( + catalog: MigrationCatalog, + selection: MigrationSelection, + previousReceipts: ReadonlyMap = new Map(), +) => { validateMigrationCatalog(catalog); let current = selection.from; const seen = new Set(); const transitions = []; for (const id of selection.path) { const declaration = catalog.migrations.find((entry) => entry.id === id); - if (!declaration || declaration.scopeId !== selection.scopeId || declaration.from !== current || seen.has(id)) throw new Error(`Invalid selected migration transition ${id}`); - for (const predecessor of declaration.predecessors) if (!seen.has(predecessor) && !previousReceipts.has(predecessor)) throw new Error(`Unsatisfied predecessor ${predecessor}`); + if (!declaration || declaration.scopeId !== selection.scopeId || declaration.from !== current || seen.has(id)) + throw new Error(`Invalid selected migration transition ${id}`); + for (const predecessor of declaration.predecessors) + if (!seen.has(predecessor) && !previousReceipts.has(predecessor)) + throw new Error(`Unsatisfied predecessor ${predecessor}`); const usedBindings: Record = {}; for (const port of declaration.ports) { if (!selection.bindings[port.name]) throw new Error(`Missing local migration binding ${port.name}`); @@ -86,7 +145,8 @@ export const selectMigrationPath = (catalog: MigrationCatalog, selection: Migrat } const digest = contentDigest({ declaration, bindings: usedBindings }); const previous = previousReceipts.get(id); - if (previous && previous !== digest) throw new Error(`Migration identity ${id} was previously used with different code or scope`); + if (previous && previous !== digest) + throw new Error(`Migration identity ${id} was previously used with different code or scope`); transitions.push({ declaration, bindings: usedBindings, digest, alreadyApplied: Boolean(previous) }); seen.add(id); current = declaration.to; diff --git a/src/capability-model/types.ts b/src/capability-model/types.ts index 15bc9e3..b5ef4c1 100644 --- a/src/capability-model/types.ts +++ b/src/capability-model/types.ts @@ -29,13 +29,11 @@ const opaque = (value: string) => value as OpaqueId; */ export const capabilityId = { workspace: (value: string) => opaque<"WorkspaceId">(value), - workspaceRevision: (value: string) => - opaque<"WorkspaceRevisionId">(value), + workspaceRevision: (value: string) => opaque<"WorkspaceRevisionId">(value), atom: (value: string) => opaque<"AtomId">(value), conformance: (value: string) => opaque<"ConformanceId">(value), interface: (value: string) => opaque<"InterfaceId">(value), - interfaceRevision: (value: string) => - opaque<"InterfaceRevisionId">(value), + interfaceRevision: (value: string) => opaque<"InterfaceRevisionId">(value), member: (value: string) => opaque<"MemberId">(value), operation: (value: string) => opaque<"OperationId">(value), slot: (value: string) => opaque<"SlotId">(value), @@ -53,15 +51,7 @@ export interface SourceRevision { commit: string; } -export type ScalarValueTypeName = - | "bool" - | "bytes" - | "double" - | "int32" - | "int64" - | "string" - | "uint32" - | "uint64"; +export type ScalarValueTypeName = "bool" | "bytes" | "double" | "int32" | "int64" | "string" | "uint32" | "uint64"; export type ObjectExpectation = | { kind: "atom"; atomId: AtomId } @@ -112,12 +102,7 @@ export interface AtomDefinition { documentation?: string; } -export type InterfaceOperationMode = - | "call" - | "watch-start" - | "watch-stop" - | "subscribe" - | "unsubscribe"; +export type InterfaceOperationMode = "call" | "watch-start" | "watch-stop" | "subscribe" | "unsubscribe"; export interface InterfaceOperation { id: OperationId; @@ -140,11 +125,7 @@ export interface ValueInterfaceMember extends InterfaceMemberBase { valueType: ValueType; } -export type EdgeCardinality = - | "optional-one" - | "exactly-one" - | "many" - | "many-unique"; +export type EdgeCardinality = "optional-one" | "exactly-one" | "many" | "many-unique"; export type EdgeEndpointConstraint = | { kind: "atom"; atomId: AtomId } @@ -167,10 +148,7 @@ export interface OperationInterfaceMember extends InterfaceMemberBase { outputType: ValueType; } -export type InterfaceMember = - | ValueInterfaceMember - | RelationshipInterfaceMember - | OperationInterfaceMember; +export type InterfaceMember = ValueInterfaceMember | RelationshipInterfaceMember | OperationInterfaceMember; export interface InterfaceRevision { interfaceId: InterfaceId; @@ -180,9 +158,7 @@ export interface InterfaceRevision { members: InterfaceMember[]; } -export type StoragePolicy = - | { kind: "optimistic-register" } - | { kind: "crdt-document"; updateType: ValueType }; +export type StoragePolicy = { kind: "optimistic-register" } | { kind: "crdt-document"; updateType: ValueType }; export interface StateSlotDefinition { kind: "state"; @@ -215,18 +191,9 @@ export interface EdgeDefinition { export type PersistentAttachment = StateSlotDefinition | EdgeDefinition; -export type StatePrimitive = - | "read" - | "write" - | "watch-start" - | "watch-stop"; +export type StatePrimitive = "read" | "write" | "watch-start" | "watch-stop"; -export type EdgePrimitive = - | "resolve" - | "connect" - | "disconnect" - | "watch-start" - | "watch-stop"; +export type EdgePrimitive = "resolve" | "connect" | "disconnect" | "watch-start" | "watch-stop"; export type PackageReceiverRequirement = | { kind: "any-object" } @@ -284,10 +251,7 @@ export interface PackageConstructorExport extends PackageExportBase { constructsAtom: AtomId; } -export type PackageExport = - | PackageOperationExport - | PackageFunctionExport - | PackageConstructorExport; +export type PackageExport = PackageOperationExport | PackageFunctionExport | PackageConstructorExport; export interface PackageRevision { migrationCatalog?: import("./migrations.js").MigrationCatalog; diff --git a/src/capability-model/validation.ts b/src/capability-model/validation.ts index 7ffdd7a..6597082 100644 --- a/src/capability-model/validation.ts +++ b/src/capability-model/validation.ts @@ -102,22 +102,16 @@ interface ValidationIndexes { conformances: Map; conformancePaths: Map; attachments: Map; - projections: Map< - string, - { edge: EdgeDefinition; endpoint: EdgeEndpoint; endpointIndex: 0 | 1 } - >; + projections: Map; constructors: Map; } const hasText = (value: string) => value.trim().length > 0; -const conformanceKey = ( - atomId: AtomId, - interfaceRevisionId: InterfaceRevisionId, -): string => `${atomId}\u0000${interfaceRevisionId}`; +const conformanceKey = (atomId: AtomId, interfaceRevisionId: InterfaceRevisionId): string => + `${atomId}\u0000${interfaceRevisionId}`; -const attachmentKey = (attachment: PersistentAttachment): string => - `${attachment.kind}\u0000${attachment.id}`; +const attachmentKey = (attachment: PersistentAttachment): string => `${attachment.kind}\u0000${attachment.id}`; const issue = ( issues: CapabilityValidationIssue[], @@ -126,12 +120,7 @@ const issue = ( message: string, ) => issues.push({ code, path, message }); -const requireText = ( - issues: CapabilityValidationIssue[], - value: string, - path: string, - label: string, -) => { +const requireText = (issues: CapabilityValidationIssue[], value: string, path: string, label: string) => { if (!hasText(value)) { issue(issues, "required-value", path, `${label} is required`); } @@ -145,7 +134,10 @@ const typeLabel = (type: ValueType): string => { case "message": return `message:${type.descriptorId}`; case "record": - return `record{${Object.keys(type.fields).sort().map((key) => `${key}:${typeLabel(type.fields[key])}`).join(";")}}`; + return `record{${Object.keys(type.fields) + .sort() + .map((key) => `${key}:${typeLabel(type.fields[key])}`) + .join(";")}}`; case "object-ref": return type.expectation.kind === "atom" ? `object:atom:${type.expectation.atomId}` @@ -169,7 +161,12 @@ export const valueTypesEqual = (left: ValueType, right: ValueType): boolean => { return left.descriptorId === (right as typeof left).descriptorId; case "record": { const other = (right as typeof left).fields; - return Object.keys(left.fields).length === Object.keys(other).length && Object.entries(left.fields).every(([key, value]) => Object.hasOwn(other, key) && valueTypesEqual(value, other[key])); + return ( + Object.keys(left.fields).length === Object.keys(other).length && + Object.entries(left.fields).every( + ([key, value]) => Object.hasOwn(other, key) && valueTypesEqual(value, other[key]), + ) + ); } case "object-ref": { const other = (right as typeof left).expectation; @@ -177,10 +174,8 @@ export const valueTypesEqual = (left: ValueType, right: ValueType): boolean => { return false; } return left.expectation.kind === "atom" - ? left.expectation.atomId === - (other as typeof left.expectation).atomId - : left.expectation.interfaceRevisionId === - (other as typeof left.expectation).interfaceRevisionId; + ? left.expectation.atomId === (other as typeof left.expectation).atomId + : left.expectation.interfaceRevisionId === (other as typeof left.expectation).interfaceRevisionId; } case "optional": case "list": @@ -188,10 +183,7 @@ export const valueTypesEqual = (left: ValueType, right: ValueType): boolean => { } }; -const constraintsEqual = ( - left: EdgeEndpointConstraint, - right: EdgeEndpointConstraint, -) => +const constraintsEqual = (left: EdgeEndpointConstraint, right: EdgeEndpointConstraint) => left.kind === right.kind && (left.kind === "atom" ? left.atomId === (right as typeof left).atomId @@ -202,19 +194,10 @@ const sourceRequiredFields: Array<[keyof SourceRevision, string]> = [ ["commit", "commit"], ]; -const validateSource = ( - issues: CapabilityValidationIssue[], - source: SourceRevision, - path: string, -) => { +const validateSource = (issues: CapabilityValidationIssue[], source: SourceRevision, path: string) => { for (const [key, label] of sourceRequiredFields) { if (!hasText(source[key])) { - issue( - issues, - "invalid-source", - `${path}.${key}`, - `Source ${label} is required`, - ); + issue(issues, "invalid-source", `${path}.${key}`, `Source ${label} is required`); } } }; @@ -228,7 +211,8 @@ const validateValueType = ( switch (type.kind) { case "record": for (const [name, field] of Object.entries(type.fields)) { - if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) issue(issues, "invalid-value-type", path, "Invalid record field name"); + if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) + issue(issues, "invalid-value-type", path, "Invalid record field name"); validateValueType(issues, field, `${path}.fields.${name}`, indexes); } return; @@ -236,12 +220,7 @@ const validateValueType = ( case "scalar": return; case "message": - requireText( - issues, - type.descriptorId, - `${path}.descriptorId`, - "Message descriptor identity", - ); + requireText(issues, type.descriptorId, `${path}.descriptorId`, "Message descriptor identity"); return; case "optional": case "list": @@ -257,9 +236,7 @@ const validateValueType = ( `Unknown atom ${type.expectation.atomId}`, ); } - } else if ( - !indexes.interfaces.has(type.expectation.interfaceRevisionId) - ) { + } else if (!indexes.interfaces.has(type.expectation.interfaceRevisionId)) { issue( issues, "unresolved-reference", @@ -278,12 +255,7 @@ const validateConstraint = ( ) => { if (constraint.kind === "atom") { if (!indexes.atoms.has(constraint.atomId)) { - issue( - issues, - "unresolved-reference", - `${path}.atomId`, - `Unknown atom ${constraint.atomId}`, - ); + issue(issues, "unresolved-reference", `${path}.atomId`, `Unknown atom ${constraint.atomId}`); } } else if (!indexes.interfaces.has(constraint.interfaceRevisionId)) { issue( @@ -295,8 +267,7 @@ const validateConstraint = ( } }; -const emitsEvents = (mode: InterfaceOperationMode) => - mode === "watch-start" || mode === "subscribe"; +const emitsEvents = (mode: InterfaceOperationMode) => mode === "watch-start" || mode === "subscribe"; const validateOperationEventShape = ( issues: CapabilityValidationIssue[], @@ -304,12 +275,7 @@ const validateOperationEventShape = ( path: string, ) => { if (emitsEvents(operation.mode) && operation.eventType === undefined) { - issue( - issues, - "invalid-operation", - `${path}.eventType`, - `${operation.mode} operations require an event type`, - ); + issue(issues, "invalid-operation", `${path}.eventType`, `${operation.mode} operations require an event type`); } if (!emitsEvents(operation.mode) && operation.eventType !== undefined) { issue( @@ -328,33 +294,21 @@ interface OperationSignature { eventType?: ValueType; } -const signaturesMatch = ( - operation: OperationSignature, - implementation: OperationSignature, -) => +const signaturesMatch = (operation: OperationSignature, implementation: OperationSignature) => operation.mode === implementation.mode && valueTypesEqual(operation.inputType, implementation.inputType) && valueTypesEqual(operation.outputType, implementation.outputType) && (operation.eventType === undefined ? implementation.eventType === undefined - : implementation.eventType !== undefined && - valueTypesEqual(operation.eventType, implementation.eventType)); + : implementation.eventType !== undefined && valueTypesEqual(operation.eventType, implementation.eventType)); const describeSignature = (signature: OperationSignature) => { - const event = signature.eventType - ? ` emits ${typeLabel(signature.eventType)}` - : ""; + const event = signature.eventType ? ` emits ${typeLabel(signature.eventType)}` : ""; return `${signature.mode} ${typeLabel(signature.inputType)} -> ${typeLabel(signature.outputType)}${event}`; }; -export const statePrimitiveSignature = ( - slot: StateSlotDefinition, - primitive: StatePrimitive, -): OperationSignature => { - const writeType = - slot.storagePolicy.kind === "crdt-document" - ? slot.storagePolicy.updateType - : slot.valueType; +export const statePrimitiveSignature = (slot: StateSlotDefinition, primitive: StatePrimitive): OperationSignature => { + const writeType = slot.storagePolicy.kind === "crdt-document" ? slot.storagePolicy.updateType : slot.valueType; switch (primitive) { case "read": return { @@ -389,10 +343,7 @@ const constraintValueType = (constraint: EdgeEndpointConstraint): ValueType => ? valueType.atomRef(constraint.atomId) : valueType.interfaceRef(constraint.interfaceRevisionId); -export const cardinalityValueType = ( - constraint: EdgeEndpointConstraint, - cardinality: EdgeCardinality, -): ValueType => { +export const cardinalityValueType = (constraint: EdgeEndpointConstraint, cardinality: EdgeCardinality): ValueType => { const target = constraintValueType(constraint); switch (cardinality) { case "exactly-one": @@ -405,13 +356,8 @@ export const cardinalityValueType = ( } }; -const edgeProjection = ( - edge: EdgeDefinition, - projectionId: EdgeProjectionId, -) => { - const index = edge.endpoints.findIndex( - (endpoint) => endpoint.projectionId === projectionId, - ); +const edgeProjection = (edge: EdgeDefinition, projectionId: EdgeProjectionId) => { + const index = edge.endpoints.findIndex((endpoint) => endpoint.projectionId === projectionId); if (index !== 0 && index !== 1) { return undefined; } @@ -433,10 +379,7 @@ export const edgePrimitiveSignature = ( return undefined; } const targetType = constraintValueType(projection.target.constraint); - const resolvedType = cardinalityValueType( - projection.target.constraint, - projection.endpoint.cardinality, - ); + const resolvedType = cardinalityValueType(projection.target.constraint, projection.endpoint.cardinality); switch (primitive) { case "resolve": return { @@ -480,21 +423,20 @@ const constraintSatisfiesConstraint = ( actual: EdgeEndpointConstraint, required: EdgeEndpointConstraint, conformances: ReadonlyMap, -) => constraintsEqual(actual, required) || ( - actual.kind === "atom" && - required.kind === "interface" && - atomSatisfiesConstraint(actual.atomId, required, conformances) -); +) => + constraintsEqual(actual, required) || + (actual.kind === "atom" && + required.kind === "interface" && + atomSatisfiesConstraint(actual.atomId, required, conformances)); const canAccessAttachment = ( owner: AttachmentOwner, conformance: Pick | undefined, ) => - owner.kind === "workspace" || ( - conformance !== undefined && + owner.kind === "workspace" || + (conformance !== undefined && owner.atomId === conformance.atomId && - owner.interfaceRevisionId === conformance.interfaceRevisionId - ); + owner.interfaceRevisionId === conformance.interfaceRevisionId); const collectIdentityIndexes = ( workspace: WorkspaceRevision, @@ -516,12 +458,7 @@ const collectIdentityIndexes = ( for (const [interfaceIndex, revision] of workspace.interfaceImports.entries()) { const path = `interfaceImports[${interfaceIndex}]`; requireText(issues, revision.interfaceId, `${path}.interfaceId`, "Interface ID"); - requireText( - issues, - revision.revisionId, - `${path}.revisionId`, - "Interface revision ID", - ); + requireText(issues, revision.revisionId, `${path}.revisionId`, "Interface revision ID"); requireText(issues, revision.displayName, `${path}.displayName`, "Interface name"); validateSource(issues, revision.source, `${path}.source`); const memberIds = new Set(); @@ -531,31 +468,16 @@ const collectIdentityIndexes = ( requireText(issues, member.id, `${memberPath}.id`, "Member ID"); requireText(issues, member.displayName, `${memberPath}.displayName`, "Member name"); if (memberIds.has(member.id)) { - issue( - issues, - "duplicate-interface-member", - `${memberPath}.id`, - `Duplicate member ${member.id}`, - ); + issue(issues, "duplicate-interface-member", `${memberPath}.id`, `Duplicate member ${member.id}`); } memberIds.add(member.id); for (const [operationIndex, operation] of member.operations.entries()) { const operationPath = `${memberPath}.operations[${operationIndex}]`; requireText(issues, operation.id, `${operationPath}.id`, "Operation ID"); - requireText( - issues, - operation.displayName, - `${operationPath}.displayName`, - "Operation name", - ); + requireText(issues, operation.displayName, `${operationPath}.displayName`, "Operation name"); validateOperationEventShape(issues, operation, operationPath); if (operations.has(operation.id)) { - issue( - issues, - "duplicate-interface-operation", - `${operationPath}.id`, - `Duplicate operation ${operation.id}`, - ); + issue(issues, "duplicate-interface-operation", `${operationPath}.id`, `Duplicate operation ${operation.id}`); } else { operations.set(operation.id, { operation, member }); } @@ -576,16 +498,19 @@ const collectIdentityIndexes = ( const packages = new Map(); for (const [packageIndex, revision] of workspace.packageImports.entries()) { const path = `packageImports[${packageIndex}]`; - if (revision.semanticMajor !== undefined && (!Number.isSafeInteger(revision.semanticMajor) || revision.semanticMajor < 1)) { - issue(issues, "invalid-semantic-major", `${path}.semanticMajor`, "Semantic major must be a positive safe integer"); + if ( + revision.semanticMajor !== undefined && + (!Number.isSafeInteger(revision.semanticMajor) || revision.semanticMajor < 1) + ) { + issue( + issues, + "invalid-semantic-major", + `${path}.semanticMajor`, + "Semantic major must be a positive safe integer", + ); } requireText(issues, revision.packageId, `${path}.packageId`, "Package ID"); - requireText( - issues, - revision.revisionId, - `${path}.revisionId`, - "Package revision ID", - ); + requireText(issues, revision.revisionId, `${path}.revisionId`, "Package revision ID"); requireText(issues, revision.displayName, `${path}.displayName`, "Package name"); validateSource(issues, revision.source, `${path}.source`); const exports = new Map(); @@ -594,12 +519,7 @@ const collectIdentityIndexes = ( requireText(issues, entry.id, `${exportPath}.id`, "Package export ID"); requireText(issues, entry.displayName, `${exportPath}.displayName`, "Package export name"); if (exports.has(entry.id)) { - issue( - issues, - "duplicate-package-export", - `${exportPath}.id`, - `Duplicate package export ${entry.id}`, - ); + issue(issues, "duplicate-package-export", `${exportPath}.id`, `Duplicate package export ${entry.id}`); } else { exports.set(entry.id, entry); } @@ -609,12 +529,7 @@ const collectIdentityIndexes = ( requireText(issues, port.id, `${portPath}.id`, "Dependency port ID"); requireText(issues, port.displayName, `${portPath}.displayName`, "Dependency port name"); if (portIds.has(port.id)) { - issue( - issues, - "duplicate-dependency-port", - `${portPath}.id`, - `Duplicate dependency port ${port.id}`, - ); + issue(issues, "duplicate-dependency-port", `${portPath}.id`, `Duplicate dependency port ${port.id}`); } portIds.add(port.id); } @@ -636,12 +551,21 @@ const collectIdentityIndexes = ( const conformanceIds = new Set(); for (const [index, conformance] of workspace.conformances.entries()) { const path = `conformances[${index}]`; - if (conformance.semanticMajor !== undefined && (!Number.isSafeInteger(conformance.semanticMajor) || conformance.semanticMajor < 1)) { - issue(issues, "invalid-semantic-major", `${path}.semanticMajor`, "Semantic major must be a positive safe integer"); + if ( + conformance.semanticMajor !== undefined && + (!Number.isSafeInteger(conformance.semanticMajor) || conformance.semanticMajor < 1) + ) { + issue( + issues, + "invalid-semantic-major", + `${path}.semanticMajor`, + "Semantic major must be a positive safe integer", + ); } if (conformance.id !== undefined) { requireText(issues, conformance.id, `${path}.id`, "Conformance ID"); - if (conformanceIds.has(conformance.id)) issue(issues, "duplicate-conformance-id", `${path}.id`, `Duplicate conformance ID ${conformance.id}`); + if (conformanceIds.has(conformance.id)) + issue(issues, "duplicate-conformance-id", `${path}.id`, `Duplicate conformance ID ${conformance.id}`); conformanceIds.add(conformance.id); } const key = conformanceKey(conformance.atomId, conformance.interfaceRevisionId); @@ -662,35 +586,18 @@ const collectIdentityIndexes = ( for (const [index, constructor] of workspace.constructors.entries()) { const path = `constructors[${index}]`; if (constructors.has(constructor.atomId)) { - issue( - issues, - "duplicate-constructor", - path, - `Atom ${constructor.atomId} has more than one constructor`, - ); + issue(issues, "duplicate-constructor", path, `Atom ${constructor.atomId} has more than one constructor`); } else { constructors.set(constructor.atomId, constructor); } } const attachments = new Map(); - const projections = new Map< - string, - { edge: EdgeDefinition; endpoint: EdgeEndpoint; endpointIndex: 0 | 1 } - >(); - const addAttachment = ( - attachment: PersistentAttachment, - owner: AttachmentOwner, - path: string, - ) => { + const projections = new Map(); + const addAttachment = (attachment: PersistentAttachment, owner: AttachmentOwner, path: string) => { const key = attachmentKey(attachment); if (attachments.has(key)) { - issue( - issues, - "duplicate-attachment", - `${path}.id`, - `Duplicate ${attachment.kind} attachment ${attachment.id}`, - ); + issue(issues, "duplicate-attachment", `${path}.id`, `Duplicate ${attachment.kind} attachment ${attachment.id}`); } else { attachments.set(key, { attachment, owner, path }); } @@ -772,34 +679,32 @@ const validateInterfaces = ( } }; -const validateAttachments = ( - issues: CapabilityValidationIssue[], - indexes: ValidationIndexes, -) => { +const validateAttachments = (issues: CapabilityValidationIssue[], indexes: ValidationIndexes) => { for (const { attachment, owner, path } of indexes.attachments.values()) { requireText(issues, attachment.id, `${path}.id`, `${attachment.kind} attachment ID`); requireText(issues, attachment.displayName, `${path}.displayName`, `${attachment.kind} name`); if (attachment.kind === "state") { if (!indexes.atoms.has(attachment.attachedTo)) { - issue( - issues, - "unresolved-reference", - `${path}.attachedTo`, - `Unknown atom ${attachment.attachedTo}`, - ); + issue(issues, "unresolved-reference", `${path}.attachedTo`, `Unknown atom ${attachment.attachedTo}`); } validateValueType(issues, attachment.valueType, `${path}.valueType`, indexes); - const containsRpcType = (type: ValueType): boolean => type.kind === "object-ref" || type.kind === "record" || ((type.kind === "optional" || type.kind === "list") && containsRpcType(type.value)); - if (containsRpcType(attachment.valueType) || (attachment.storagePolicy.kind === "crdt-document" && containsRpcType(attachment.storagePolicy.updateType))) { - issue(issues, "invalid-attachment", `${path}.valueType`, "Managed object references belong in graph relationships, not ordinary state; record types are RPC-only"); + const containsRpcType = (type: ValueType): boolean => + type.kind === "object-ref" || + type.kind === "record" || + ((type.kind === "optional" || type.kind === "list") && containsRpcType(type.value)); + if ( + containsRpcType(attachment.valueType) || + (attachment.storagePolicy.kind === "crdt-document" && containsRpcType(attachment.storagePolicy.updateType)) + ) { + issue( + issues, + "invalid-attachment", + `${path}.valueType`, + "Managed object references belong in graph relationships, not ordinary state; record types are RPC-only", + ); } if (attachment.storagePolicy.kind === "crdt-document") { - validateValueType( - issues, - attachment.storagePolicy.updateType, - `${path}.storagePolicy.updateType`, - indexes, - ); + validateValueType(issues, attachment.storagePolicy.updateType, `${path}.storagePolicy.updateType`, indexes); } if (owner.kind === "conformance") { if (attachment.attachedTo !== owner.atomId) { @@ -815,26 +720,42 @@ const validateAttachments = ( } if (attachment.endpoints.length !== 2) { - issue( - issues, - "invalid-attachment", - `${path}.endpoints`, - `Edge ${attachment.id} must have exactly two endpoints`, - ); + issue(issues, "invalid-attachment", `${path}.endpoints`, `Edge ${attachment.id} must have exactly two endpoints`); } for (const [endpointIndex, endpoint] of attachment.endpoints.entries()) { const endpointPath = `${path}.endpoints[${endpointIndex}]`; - if (endpoint.keyType !== undefined && (!["string", "boolean", "int64"].includes(endpoint.keyType) || endpoint.ordered || !["many", "many-unique"].includes(endpoint.cardinality))) { - issue(issues, "invalid-attachment", `${endpointPath}.keyType`, "Keyed projections require string/boolean/int64 keys, many cardinality, and no ordering"); + if ( + endpoint.keyType !== undefined && + (!["string", "boolean", "int64"].includes(endpoint.keyType) || + endpoint.ordered || + !["many", "many-unique"].includes(endpoint.cardinality)) + ) { + issue( + issues, + "invalid-attachment", + `${endpointPath}.keyType`, + "Keyed projections require string/boolean/int64 keys, many cardinality, and no ordering", + ); } requireText(issues, endpoint.projectionId, `${endpointPath}.projectionId`, "Projection ID"); if (endpoint.onDelete !== undefined && !["restrict", "detach", "cascade-other"].includes(endpoint.onDelete)) { - issue(issues, "invalid-attachment", `${endpointPath}.onDelete`, "Deletion policy must be restrict, detach, or cascade-other"); + issue( + issues, + "invalid-attachment", + `${endpointPath}.onDelete`, + "Deletion policy must be restrict, detach, or cascade-other", + ); } requireText(issues, endpoint.displayName, `${endpointPath}.displayName`, "Projection name"); validateConstraint(issues, endpoint.constraint, `${endpointPath}.constraint`, indexes); } - if (attachment.endpoints.every((endpoint) => endpoint.keyType)) issue(issues, "invalid-attachment", `${path}.endpoints`, "A v0 map has one keyed projection and one unkeyed inverse"); + if (attachment.endpoints.every((endpoint) => endpoint.keyType)) + issue( + issues, + "invalid-attachment", + `${path}.endpoints`, + "A v0 map has one keyed projection and one unkeyed inverse", + ); if (owner.kind === "conformance") { if ( !attachment.endpoints.some((endpoint) => @@ -852,23 +773,14 @@ const validateAttachments = ( } }; -const uniquePrimitiveList = ( - issues: CapabilityValidationIssue[], - primitives: readonly string[], - path: string, -) => { +const uniquePrimitiveList = (issues: CapabilityValidationIssue[], primitives: readonly string[], path: string) => { if (primitives.length === 0) { issue(issues, "invalid-dependency-binding", path, "Dependency port requires at least one primitive"); } const seen = new Set(); for (const primitive of primitives) { if (seen.has(primitive)) { - issue( - issues, - "invalid-dependency-binding", - path, - `Dependency primitive ${primitive} is listed more than once`, - ); + issue(issues, "invalid-dependency-binding", path, `Dependency primitive ${primitive} is listed more than once`); } seen.add(primitive); } @@ -901,8 +813,10 @@ const validatePackages = ( } } else if (entry.receiverRequirement.kind === "all-interfaces") { const seen = new Set(); - for (const [requirementIndex, interfaceRevisionId] of - entry.receiverRequirement.interfaceRevisionIds.entries()) { + for (const [ + requirementIndex, + interfaceRevisionId, + ] of entry.receiverRequirement.interfaceRevisionIds.entries()) { const requirementPath = `${exportPath}.receiverRequirement.interfaceRevisionIds[${requirementIndex}]`; if (!indexes.interfaces.has(interfaceRevisionId)) { issue( @@ -925,12 +839,7 @@ const validatePackages = ( } } else if (entry.kind === "constructor") { if (!indexes.atoms.has(entry.constructsAtom)) { - issue( - issues, - "unresolved-reference", - `${exportPath}.constructsAtom`, - `Unknown atom ${entry.constructsAtom}`, - ); + issue(issues, "unresolved-reference", `${exportPath}.constructsAtom`, `Unknown atom ${entry.constructsAtom}`); } const expected = valueType.atomRef(entry.constructsAtom); if (!valueTypesEqual(entry.outputType, expected)) { @@ -965,7 +874,8 @@ const validatePackages = ( } break; case "constructor": - if (port.requirement.inputType) validateValueType(issues, port.requirement.inputType, `${portPath}.requirement.inputType`, indexes); + if (port.requirement.inputType) + validateValueType(issues, port.requirement.inputType, `${portPath}.requirement.inputType`, indexes); if (!indexes.atoms.has(port.requirement.atomId)) { issue( issues, @@ -998,9 +908,7 @@ const validateAttachmentAccess = ( "private-attachment-access", path, `Attachment ${entry.attachment.id} is private to conformance ${ - entry.owner.kind === "conformance" - ? `${entry.owner.atomId} as ${entry.owner.interfaceRevisionId}` - : "" + entry.owner.kind === "conformance" ? `${entry.owner.atomId} as ${entry.owner.interfaceRevisionId}` : "" }`, ); return false; @@ -1018,12 +926,7 @@ const validateTraversal = ( ) => { const attachment = findAttachment(indexes, "edge", traversal.edgeTypeId); if (!attachment || attachment.attachment.kind !== "edge") { - issue( - issues, - "invalid-dependency-binding", - `${path}.edgeTypeId`, - `Unknown traversal edge ${traversal.edgeTypeId}`, - ); + issue(issues, "invalid-dependency-binding", `${path}.edgeTypeId`, `Unknown traversal edge ${traversal.edgeTypeId}`); return undefined; } const projection = edgeProjection(attachment.attachment, traversal.projectionId); @@ -1036,7 +939,8 @@ const validateTraversal = ( ); return undefined; } - if (!projection.endpoint.publicTraversal) validateAttachmentAccess(issues, attachment, conformance, `${path}.edgeTypeId`); + if (!projection.endpoint.publicTraversal) + validateAttachmentAccess(issues, attachment, conformance, `${path}.edgeTypeId`); if (!atomSatisfiesConstraint(atomId, projection.endpoint.constraint, indexes.conformances)) { issue( issues, @@ -1085,12 +989,7 @@ const validateBoundDependencies = ( bindings.set(dependency.portId, dependency); const port = ports.get(dependency.portId); if (!port) { - issue( - issues, - "invalid-dependency-binding", - `${path}.portId`, - `Unknown dependency port ${dependency.portId}`, - ); + issue(issues, "invalid-dependency-binding", `${path}.portId`, `Unknown dependency port ${dependency.portId}`); continue; } const requirement = port.requirement; @@ -1130,7 +1029,8 @@ const validateBoundDependencies = ( : undefined; if ( stateBinding.via - ? traversal && !atomSatisfiesConstraint( + ? traversal && + !atomSatisfiesConstraint( attachment.attachment.attachedTo, traversal.target.constraint, params.indexes.conformances, @@ -1190,20 +1090,15 @@ const validateBoundDependencies = ( ) : undefined; const originMatches = edgeBinding.via - ? traversal && ( - projection.endpoint.constraint.kind === "atom" - ? atomSatisfiesConstraint( - projection.endpoint.constraint.atomId, - traversal.target.constraint, - params.indexes.conformances, - ) - : constraintsEqual(projection.endpoint.constraint, traversal.target.constraint) - ) - : atomSatisfiesConstraint( - params.atomId, - projection.endpoint.constraint, - params.indexes.conformances, - ); + ? traversal && + (projection.endpoint.constraint.kind === "atom" + ? atomSatisfiesConstraint( + projection.endpoint.constraint.atomId, + traversal.target.constraint, + params.indexes.conformances, + ) + : constraintsEqual(projection.endpoint.constraint, traversal.target.constraint)) + : atomSatisfiesConstraint(params.atomId, projection.endpoint.constraint, params.indexes.conformances); if (!originMatches) { issue( issues, @@ -1232,10 +1127,7 @@ const validateBoundDependencies = ( break; } case "interface": { - const interfaceBinding = binding as Extract< - DependencyBinding, - { kind: "interface" } - >; + const interfaceBinding = binding as Extract; if (interfaceBinding.interfaceRevisionId !== requirement.interfaceRevisionId) { issue( issues, @@ -1257,12 +1149,9 @@ const validateBoundDependencies = ( : undefined; const candidateAtoms = interfaceBinding.via ? traversal - ? [...params.indexes.atoms.keys()].filter((atomId) => - atomSatisfiesConstraint( - atomId as AtomId, - traversal.target.constraint, - params.indexes.conformances, - )) as AtomId[] + ? ([...params.indexes.atoms.keys()].filter((atomId) => + atomSatisfiesConstraint(atomId as AtomId, traversal.target.constraint, params.indexes.conformances), + ) as AtomId[]) : [] : [params.atomId]; for (const atomId of candidateAtoms) { @@ -1300,10 +1189,16 @@ const validateBoundDependencies = ( } if (requirement.inputType) { const selected = params.indexes.constructors.get(requirement.atomId); - const implementation = selected ? params.indexes.packages.get(selected.packageRevisionId)?.exports.get(selected.exportId) : undefined; + const implementation = selected + ? params.indexes.packages.get(selected.packageRevisionId)?.exports.get(selected.exportId) + : undefined; if (implementation && !valueTypesEqual(requirement.inputType, implementation.inputType)) { - issue(issues, "invalid-dependency-binding", `${path}.binding.atomId`, - `Constructor port requires input ${typeLabel(requirement.inputType)}, but selected constructor accepts ${typeLabel(implementation.inputType)}`); + issue( + issues, + "invalid-dependency-binding", + `${path}.binding.atomId`, + `Constructor port requires input ${typeLabel(requirement.inputType)}, but selected constructor accepts ${typeLabel(implementation.inputType)}`, + ); } } break; @@ -1373,12 +1268,7 @@ const validateConformances = ( const path = `conformances[${index}]`; const key = conformanceKey(conformance.atomId, conformance.interfaceRevisionId); if (!indexes.atoms.has(conformance.atomId)) { - issue( - issues, - "unresolved-reference", - `${path}.atomId`, - `Unknown atom ${conformance.atomId}`, - ); + issue(issues, "unresolved-reference", `${path}.atomId`, `Unknown atom ${conformance.atomId}`); } const interfaceEntry = indexes.interfaces.get(conformance.interfaceRevisionId); if (!interfaceEntry) { @@ -1473,9 +1363,7 @@ const validateConformances = ( if (!(binding.primitive === "resolve" && projection?.endpoint.publicTraversal)) { validateAttachmentAccess(issues, attachment, conformance, `${bindingPath}.binding.edgeTypeId`); } - const relationshipMember = operationEntry.member.kind === "relationship" - ? operationEntry.member - : undefined; + const relationshipMember = operationEntry.member.kind === "relationship" ? operationEntry.member : undefined; const operationEdge = relationshipMember ? { ...attachment.attachment, @@ -1486,11 +1374,7 @@ const validateConformances = ( ) as [EdgeEndpoint, EdgeEndpoint], } : attachment.attachment; - const expected = edgePrimitiveSignature( - operationEdge, - binding.projectionId, - binding.primitive, - ); + const expected = edgePrimitiveSignature(operationEdge, binding.projectionId, binding.primitive); if (!projection || !expected) { issue( issues, @@ -1599,10 +1483,8 @@ const validateConformances = ( } const materializedMembers = new Set(); - for (const [materializationIndex, materialization] of - (conformance.relationshipMaterializations ?? []).entries()) { - const materializationPath = - `${path}.relationshipMaterializations[${materializationIndex}]`; + for (const [materializationIndex, materialization] of (conformance.relationshipMaterializations ?? []).entries()) { + const materializationPath = `${path}.relationshipMaterializations[${materializationIndex}]`; if (materializedMembers.has(materialization.memberId)) { issue( issues, @@ -1613,8 +1495,7 @@ const validateConformances = ( continue; } materializedMembers.add(materialization.memberId); - const member = interfaceEntry.revision.members.find((entry) => - entry.id === materialization.memberId); + const member = interfaceEntry.revision.members.find((entry) => entry.id === materialization.memberId); if (!member || member.kind !== "relationship") { issue( issues, @@ -1632,11 +1513,8 @@ const validateConformances = ( "A lazily constructed relationship must be optional-one before construction", ); } - const resolveOperation = member.operations.find((entry) => - entry.displayName === "resolve"); - const resolveBinding = resolveOperation - ? bindings.get(resolveOperation.id) - : undefined; + const resolveOperation = member.operations.find((entry) => entry.displayName === "resolve"); + const resolveBinding = resolveOperation ? bindings.get(resolveOperation.id) : undefined; if (!resolveBinding || resolveBinding.kind !== "edge") { issue( issues, @@ -1664,22 +1542,14 @@ const validateConformances = ( ); continue; } - validateAttachmentAccess( - issues, - attachment, - conformance, - `${materializationPath}.edgeTypeId`, - ); - const hostProjection = edgeProjection( - attachment.attachment, - resolveBinding.projectionId, - ); - const constructedProjection = edgeProjection( - attachment.attachment, - materialization.constructedProjectionId, - ); - if (!hostProjection || !constructedProjection || - hostProjection.endpointIndex === constructedProjection.endpointIndex) { + validateAttachmentAccess(issues, attachment, conformance, `${materializationPath}.edgeTypeId`); + const hostProjection = edgeProjection(attachment.attachment, resolveBinding.projectionId); + const constructedProjection = edgeProjection(attachment.attachment, materialization.constructedProjectionId); + if ( + !hostProjection || + !constructedProjection || + hostProjection.endpointIndex === constructedProjection.endpointIndex + ) { issue( issues, "invalid-relationship-materialization", @@ -1688,11 +1558,7 @@ const validateConformances = ( ); continue; } - if (!atomSatisfiesConstraint( - materialization.constructorAtomId, - member.target, - indexes.conformances, - )) { + if (!atomSatisfiesConstraint(materialization.constructorAtomId, member.target, indexes.conformances)) { issue( issues, "invalid-relationship-materialization", @@ -1700,15 +1566,14 @@ const validateConformances = ( `Constructed atom ${materialization.constructorAtomId} does not satisfy the relationship target`, ); } - if (!atomSatisfiesConstraint( - materialization.constructorAtomId, - constructedProjection.endpoint.constraint, - indexes.conformances, - ) || !atomSatisfiesConstraint( - conformance.atomId, - constructedProjection.target.constraint, - indexes.conformances, - )) { + if ( + !atomSatisfiesConstraint( + materialization.constructorAtomId, + constructedProjection.endpoint.constraint, + indexes.conformances, + ) || + !atomSatisfiesConstraint(conformance.atomId, constructedProjection.target.constraint, indexes.conformances) + ) { issue( issues, "invalid-relationship-materialization", @@ -1727,10 +1592,7 @@ const validateConformances = ( `${materializationPath}.constructorAtomId`, `Constructed atom ${materialization.constructorAtomId} has no valid constructor`, ); - } else if (!valueTypesEqual( - constructorExport.inputType, - valueType.atomRef(conformance.atomId), - )) { + } else if (!valueTypesEqual(constructorExport.inputType, valueType.atomRef(conformance.atomId))) { issue( issues, "invalid-relationship-materialization", @@ -1803,12 +1665,7 @@ const validateConstructors = ( continue; } if (!packageExport) { - issue( - issues, - "invalid-constructor", - `${path}.exportId`, - `Unknown export ${constructor.exportId}`, - ); + issue(issues, "invalid-constructor", `${path}.exportId`, `Unknown export ${constructor.exportId}`); continue; } if (packageExport.kind !== "constructor") { @@ -1839,9 +1696,7 @@ const validateConstructors = ( } }; -export const validateWorkspaceRevision = ( - workspace: WorkspaceRevision, -): CapabilityValidationIssue[] => { +export const validateWorkspaceRevision = (workspace: WorkspaceRevision): CapabilityValidationIssue[] => { const issues: CapabilityValidationIssue[] = []; requireText(issues, workspace.id, "id", "Workspace revision ID"); requireText(issues, workspace.workspaceId, "workspaceId", "Workspace ID"); @@ -1874,9 +1729,7 @@ export type CompileWorkspaceRevisionResult = | { ok: true; plan: CompiledWorkspaceRevision } | { ok: false; issues: CapabilityValidationIssue[] }; -export const compileWorkspaceRevision = ( - workspace: WorkspaceRevision, -): CompileWorkspaceRevisionResult => { +export const compileWorkspaceRevision = (workspace: WorkspaceRevision): CompileWorkspaceRevisionResult => { const snapshot = structuredClone(workspace) as WorkspaceRevision; const issues = validateWorkspaceRevision(snapshot); if (issues.length > 0) { @@ -1902,27 +1755,18 @@ export const compileWorkspaceRevision = ( }, }); } - conformances.set( - conformanceKey(conformance.atomId, conformance.interfaceRevisionId), - { - source: conformance, - operationBindings: new Map( - conformance.operationBindings.map((entry) => [entry.operationId, entry.binding]), - ), - }, - ); + conformances.set(conformanceKey(conformance.atomId, conformance.interfaceRevisionId), { + source: conformance, + operationBindings: new Map(conformance.operationBindings.map((entry) => [entry.operationId, entry.binding])), + }); } return { ok: true, plan: { source: snapshot, atoms: new Map(snapshot.atoms.map((atom) => [atom.id, atom])), - interfaces: new Map( - snapshot.interfaceImports.map((revision) => [revision.revisionId, revision]), - ), - packages: new Map( - snapshot.packageImports.map((revision) => [revision.revisionId, revision]), - ), + interfaces: new Map(snapshot.interfaceImports.map((revision) => [revision.revisionId, revision])), + packages: new Map(snapshot.packageImports.map((revision) => [revision.revisionId, revision])), attachments, conformances, constructors: new Map(snapshot.constructors.map((entry) => [entry.atomId, entry])), @@ -1934,18 +1778,14 @@ export const resolveConformance = ( plan: CompiledWorkspaceRevision, atomId: AtomId, interfaceRevisionId: InterfaceRevisionId, -): CompiledConformance | undefined => - plan.conformances.get(conformanceKey(atomId, interfaceRevisionId)); +): CompiledConformance | undefined => plan.conformances.get(conformanceKey(atomId, interfaceRevisionId)); export const resolveOperationBinding = ( plan: CompiledWorkspaceRevision, atomId: AtomId, interfaceRevisionId: InterfaceRevisionId, operationId: OperationId, -): Binding | undefined => - resolveConformance(plan, atomId, interfaceRevisionId)?.operationBindings.get( - operationId, - ); +): Binding | undefined => resolveConformance(plan, atomId, interfaceRevisionId)?.operationBindings.get(operationId); export type ResolvedOperationPlan = | { @@ -2005,8 +1845,7 @@ export const resolveOperationPlan = ( } const packageRevision = plan.packages.get(binding.packageRevisionId); const packageExport = packageRevision?.exports.find( - (entry): entry is PackageOperationExport => - entry.id === binding.exportId && entry.kind === "operation", + (entry): entry is PackageOperationExport => entry.id === binding.exportId && entry.kind === "operation", ); if (!packageRevision || !packageExport) { return undefined; @@ -2038,23 +1877,21 @@ export const computeCapabilityClosure = ( plan: CompiledWorkspaceRevision, roots: Array<{ atomId: AtomId; interfaceRevisionId: InterfaceRevisionId }>, ): CapabilityClosure => { - const conformances = new Map(); + const conformances = new Map< + string, + { + atomId: AtomId; + interfaceRevisionId: InterfaceRevisionId; + } + >(); const packages = new Set(); const attachments = new Set(); const constructors = new Set(); const queued = [...roots]; const visited = new Set(); - const sourceConformances = new Map( - [...plan.conformances].map(([key, value]) => [key, value.source]), - ); + const sourceConformances = new Map([...plan.conformances].map(([key, value]) => [key, value.source])); - const includeDependencies = ( - atomId: AtomId, - dependencies: readonly BoundDependency[], - ) => { + const includeDependencies = (atomId: AtomId, dependencies: readonly BoundDependency[]) => { for (const dependency of dependencies) { switch (dependency.binding.kind) { case "state": @@ -2075,21 +1912,13 @@ export const computeCapabilityClosure = ( } const targetAtoms = dependency.binding.via ? (() => { - const attachment = plan.attachments.get( - `edge\u0000${dependency.binding.via!.edgeTypeId}`, - )?.attachment; + const attachment = plan.attachments.get(`edge\u0000${dependency.binding.via!.edgeTypeId}`)?.attachment; if (!attachment || attachment.kind !== "edge") return []; - const projection = edgeProjection( - attachment, - dependency.binding.via!.projectionId, - ); + const projection = edgeProjection(attachment, dependency.binding.via!.projectionId); return projection ? [...plan.atoms.keys()].filter((candidate) => - atomSatisfiesConstraint( - candidate, - projection.target.constraint, - sourceConformances, - )) + atomSatisfiesConstraint(candidate, projection.target.constraint, sourceConformances), + ) : []; })() : [atomId]; @@ -2138,19 +1967,16 @@ export const computeCapabilityClosure = ( includeDependencies(root.atomId, binding.dependencies); const packageRevision = plan.packages.get(binding.packageRevisionId); const operation = packageRevision?.exports.find( - (entry): entry is PackageOperationExport => - entry.id === binding.exportId && entry.kind === "operation", + (entry): entry is PackageOperationExport => entry.id === binding.exportId && entry.kind === "operation", ); if (operation?.receiverRequirement.kind === "all-interfaces") { - for (const interfaceRevisionId of operation.receiverRequirement - .interfaceRevisionIds) { + for (const interfaceRevisionId of operation.receiverRequirement.interfaceRevisionIds) { queued.push({ atomId: root.atomId, interfaceRevisionId }); } } } } - for (const materialization of - conformance.source.relationshipMaterializations ?? []) { + for (const materialization of conformance.source.relationshipMaterializations ?? []) { attachments.add(materialization.edgeTypeId); constructors.add(materialization.constructorAtomId); const constructor = plan.constructors.get(materialization.constructorAtomId); diff --git a/src/descriptor-check.ts b/src/descriptor-check.ts index 031038a..433d87b 100644 --- a/src/descriptor-check.ts +++ b/src/descriptor-check.ts @@ -1,10 +1,6 @@ #!/usr/bin/env node import fs from "node:fs"; -import { - packageDescriptorToJson, - parsePackageDescriptorTextproto, - validatePackageDescriptor, -} from "./descriptor.js"; +import { packageDescriptorToJson, parsePackageDescriptorTextproto, validatePackageDescriptor } from "./descriptor.js"; const usage = (): never => { console.error("Usage: quixos-descriptor-check "); @@ -14,9 +10,7 @@ const usage = (): never => { const path = process.argv[2] ?? usage(); try { - const descriptor = parsePackageDescriptorTextproto( - fs.readFileSync(path, "utf8"), - ); + const descriptor = parsePackageDescriptorTextproto(fs.readFileSync(path, "utf8")); const errors = validatePackageDescriptor(descriptor); if (errors.length > 0) { for (const error of errors) { diff --git a/src/descriptor.ts b/src/descriptor.ts index f4709fd..6477228 100644 --- a/src/descriptor.ts +++ b/src/descriptor.ts @@ -14,19 +14,13 @@ const protoPaths = () => { path.resolve(process.cwd(), "proto"), path.resolve(process.cwd(), "quixos-protocol/proto"), ]; - return [...new Set(candidates)].filter((candidate) => - fs.existsSync(path.join(candidate, "quixos/package.proto")), - ); + return [...new Set(candidates)].filter((candidate) => fs.existsSync(path.join(candidate, "quixos/package.proto"))); }; -export const parsePackageDescriptorTextproto = ( - text: string, -): PackageDescriptor => { +export const parsePackageDescriptorTextproto = (text: string): PackageDescriptor => { const paths = protoPaths(); if (paths.length === 0) { - throw new Error( - "QUIXOS_PROTO_PATH must include quixos-protocol/proto to parse package descriptors", - ); + throw new Error("QUIXOS_PROTO_PATH must include quixos-protocol/proto to parse package descriptors"); } const result = childProcess.spawnSync( "protoc", @@ -44,9 +38,7 @@ export const parsePackageDescriptorTextproto = ( throw result.error; } if (result.status !== 0) { - throw new Error( - `Failed to parse package descriptor textproto:\n${result.stderr.toString().trim()}`, - ); + throw new Error(`Failed to parse package descriptor textproto:\n${result.stderr.toString().trim()}`); } return fromBinary(PackageDescriptorSchema, result.stdout); }; @@ -56,9 +48,7 @@ export const packageDescriptorToJson = (descriptor: PackageDescriptor) => prettySpaces: 2, }); -export const validatePackageDescriptor = ( - descriptor: PackageDescriptor, -): string[] => { +export const validatePackageDescriptor = (descriptor: PackageDescriptor): string[] => { const errors: string[] = []; if (!descriptor.packageId) { errors.push("packageId is required"); diff --git a/src/resource-lock/loader.ts b/src/resource-lock/loader.ts index fed7c9f..7f134e9 100644 --- a/src/resource-lock/loader.ts +++ b/src/resource-lock/loader.ts @@ -1,24 +1,11 @@ import { lstat, readFile } from "node:fs/promises"; import path from "node:path"; -import { - parseQuixosLockDocument, - type QuixosLockDiagnostic, - type QuixosLockParseResult, -} from "./parser.js"; -import type { - LockedResource, - QuixosLockDocument, - QuixosRepositoryLock, -} from "./types.js"; +import { parseQuixosLockDocument, type QuixosLockDiagnostic, type QuixosLockParseResult } from "./parser.js"; +import type { LockedResource, QuixosLockDocument, QuixosRepositoryLock } from "./types.js"; export type QuixosLockSourceReader = (relativePath: string) => Promise; -const diagnostic = ( - code: string, - message: string, - fileName: string, - path?: string, -): QuixosLockDiagnostic => ({ +const diagnostic = (code: string, message: string, fileName: string, path?: string): QuixosLockDiagnostic => ({ phase: "resolution", code, message, @@ -38,11 +25,9 @@ export const resolveQuixosLock = async ( if (root.document.kind !== "root") { return { ok: false, - diagnostics: [diagnostic( - "expected-root-lock", - "The entrypoint must be a root Quixos lock, not a fragment", - rootFileName, - )], + diagnostics: [ + diagnostic("expected-root-lock", "The entrypoint must be a root Quixos lock, not a fragment", rootFileName), + ], }; } @@ -54,15 +39,16 @@ export const resolveQuixosLock = async ( const addResources = (document: QuixosLockDocument, fileName: string) => { for (const resource of document.resources) { - const duplicate = resources.find((entry) => - entry.kind === resource.kind && entry.binding === resource.binding); + const duplicate = resources.find((entry) => entry.kind === resource.kind && entry.binding === resource.binding); if (duplicate) { - diagnostics.push(diagnostic( - "duplicate-resource-binding", - `Duplicate ${resource.kind} binding ${resource.binding} across imported lock files`, - fileName, - `${resource.kind}.${resource.binding}`, - )); + diagnostics.push( + diagnostic( + "duplicate-resource-binding", + `Duplicate ${resource.kind} binding ${resource.binding} across imported lock files`, + fileName, + `${resource.kind}.${resource.binding}`, + ), + ); } else { resources.push(resource); } @@ -71,11 +57,9 @@ export const resolveQuixosLock = async ( const visit = async (importPath: string) => { if (active.includes(importPath)) { - diagnostics.push(diagnostic( - "import-cycle", - `Lock import cycle: ${[...active, importPath].join(" -> ")}`, - importPath, - )); + diagnostics.push( + diagnostic("import-cycle", `Lock import cycle: ${[...active, importPath].join(" -> ")}`, importPath), + ); return; } if (visited.has(importPath)) return; @@ -84,11 +68,13 @@ export const resolveQuixosLock = async ( try { source = await readSource(importPath); } catch (cause) { - diagnostics.push(diagnostic( - "import-read-failed", - `Could not read lock import ${importPath}: ${cause instanceof Error ? cause.message : String(cause)}`, - importPath, - )); + diagnostics.push( + diagnostic( + "import-read-failed", + `Could not read lock import ${importPath}: ${cause instanceof Error ? cause.message : String(cause)}`, + importPath, + ), + ); active.pop(); return; } @@ -99,11 +85,13 @@ export const resolveQuixosLock = async ( return; } if (parsed.document.kind !== "fragment") { - diagnostics.push(diagnostic( - "imported-root-lock", - `Imported file ${importPath} must begin with "quixos-lock fragment"`, - importPath, - )); + diagnostics.push( + diagnostic( + "imported-root-lock", + `Imported file ${importPath} must begin with "quixos-lock fragment"`, + importPath, + ), + ); active.pop(); return; } @@ -131,20 +119,24 @@ export const loadQuixosLock = async (fileName: string): Promise { - let absoluteImport = repositoryRoot; - const segments = relativePath.split("/"); - for (const [index, segment] of segments.entries()) { - absoluteImport = path.join(absoluteImport, segment); - const metadata = await lstat(absoluteImport); - if (metadata.isSymbolicLink()) { - throw new Error("imports must not traverse symbolic links"); + return await resolveQuixosLock( + rootSource, + async (relativePath) => { + let absoluteImport = repositoryRoot; + const segments = relativePath.split("/"); + for (const [index, segment] of segments.entries()) { + absoluteImport = path.join(absoluteImport, segment); + const metadata = await lstat(absoluteImport); + if (metadata.isSymbolicLink()) { + throw new Error("imports must not traverse symbolic links"); + } + const final = index === segments.length - 1; + if ((!final && !metadata.isDirectory()) || (final && !metadata.isFile())) { + throw new Error("imports must be ordinary files beneath ordinary directories"); + } } - const final = index === segments.length - 1; - if ((!final && !metadata.isDirectory()) || (final && !metadata.isFile())) { - throw new Error("imports must be ordinary files beneath ordinary directories"); - } - } - return await readFile(absoluteImport, "utf8"); - }, rootName); + return await readFile(absoluteImport, "utf8"); + }, + rootName, + ); }; diff --git a/src/resource-lock/parser.ts b/src/resource-lock/parser.ts index 9d2388a..4e633af 100644 --- a/src/resource-lock/parser.ts +++ b/src/resource-lock/parser.ts @@ -13,13 +13,7 @@ import { type QuixosSourceBlockContext, type SourceBlockContext, } from "./generated/QuixosLockParser.js"; -import type { - GitSource, - LockedResource, - QuixosLockDocument, - QuixosRepositoryLock, - QuixosSource, -} from "./types.js"; +import type { GitSource, LockedResource, QuixosLockDocument, QuixosRepositoryLock, QuixosSource } from "./types.js"; export type QuixosLockDiagnostic = { phase: "syntax" | "validation" | "resolution"; @@ -66,8 +60,7 @@ class SyntaxErrorListener extends BaseErrorListener { } } -const stringValue = (context: { getText(): string }): string => - JSON.parse(context.getText()) as string; +const stringValue = (context: { getText(): string }): string => JSON.parse(context.getText()) as string; const lowerSource = (context: SourceBlockContext): GitSource => ({ resolver: "git", @@ -103,15 +96,16 @@ const issue = ( path?: string, line = 1, column = 0, -) => diagnostics.push({ - phase: "validation", - code, - message, - fileName, - line, - column, - path, -}); +) => + diagnostics.push({ + phase: "validation", + code, + message, + fileName, + line, + column, + path, + }); const validateImport = ( importPath: string, @@ -123,11 +117,11 @@ const validateImport = ( ) => { const path = `imports[${index}]`; if ( - !importPath - || importPath.startsWith("/") - || importPath.includes("\\") - || importPath.split("/").some((segment) => !segment || segment === "." || segment === "..") - || /^[A-Za-z][A-Za-z0-9+.-]*:/.test(importPath) + !importPath || + importPath.startsWith("/") || + importPath.includes("\\") || + importPath.split("/").some((segment) => !segment || segment === "." || segment === "..") || + /^[A-Za-z][A-Za-z0-9+.-]*:/.test(importPath) ) { issue( diagnostics, @@ -141,12 +135,7 @@ const validateImport = ( } }; -const validateSource = ( - source: GitSource, - path: string, - fileName: string, - diagnostics: QuixosLockDiagnostic[], -) => { +const validateSource = (source: GitSource, path: string, fileName: string, diagnostics: QuixosLockDiagnostic[]) => { if (!/^([0-9a-f]{40}|[0-9a-f]{64})$/.test(source.commit)) { issue( diagnostics, @@ -198,25 +187,22 @@ const validateSource = ( } }; -const validateQuixosSource = ( - source: QuixosSource, - fileName: string, - diagnostics: QuixosLockDiagnostic[], -) => { +const validateQuixosSource = (source: QuixosSource, fileName: string, diagnostics: QuixosLockDiagnostic[]) => { validateSource(source, "quixos", fileName, diagnostics); if (!source.policy) return; const forbiddenRefCharacters = new Set("~^:?*[\\"); - const invalidRef = !source.ref - || [...source.ref].some((character) => { + const invalidRef = + !source.ref || + [...source.ref].some((character) => { const code = character.charCodeAt(0); return code <= 0x20 || code === 0x7f || forbiddenRefCharacters.has(character); - }) - || source.ref.startsWith("/") - || source.ref.endsWith("/") - || source.ref.endsWith(".") - || source.ref.includes("..") - || source.ref.includes("@{") - || source.ref.includes("//"); + }) || + source.ref.startsWith("/") || + source.ref.endsWith("/") || + source.ref.endsWith(".") || + source.ref.includes("..") || + source.ref.includes("@{") || + source.ref.includes("//"); if (invalidRef) { issue( diagnostics, @@ -247,10 +233,7 @@ const validateQuixosSource = ( } }; -export const parseQuixosLockDocument = ( - source: string, - fileName = "", -): QuixosLockDocumentParseResult => { +export const parseQuixosLockDocument = (source: string, fileName = ""): QuixosLockDocumentParseResult => { const diagnostics: QuixosLockDiagnostic[] = []; const listener = new SyntaxErrorListener(fileName, diagnostics); const lexer = new QuixosLockLexer(CharStream.fromString(source)); @@ -299,26 +282,13 @@ export const parseQuixosLockDocument = ( const imports = tree.importEntry().map((context, index) => { const importPath = stringValue(context.stringLiteral()); - validateImport( - importPath, - index, - fileName, - diagnostics, - context.start?.line ?? 1, - context.start?.column ?? 0, - ); + validateImport(importPath, index, fileName, diagnostics, context.start?.line ?? 1, context.start?.column ?? 0); return importPath; }); const repeatedImports = new Set(); imports.forEach((importPath, index) => { if (repeatedImports.has(importPath)) { - issue( - diagnostics, - fileName, - "duplicate-import", - `Duplicate lock import ${importPath}`, - `imports[${index}]`, - ); + issue(diagnostics, fileName, "duplicate-import", `Duplicate lock import ${importPath}`, `imports[${index}]`); } repeatedImports.add(importPath); }); @@ -355,36 +325,38 @@ export const parseQuixosLockDocument = ( }; }; -export const parseQuixosLock = ( - source: string, - fileName = "", -): QuixosLockParseResult => { +export const parseQuixosLock = (source: string, fileName = ""): QuixosLockParseResult => { const parsed = parseQuixosLockDocument(source, fileName); if (!parsed.ok) return parsed; if (parsed.document.kind === "fragment") { return { ok: false, - diagnostics: [{ - phase: "validation", - code: "expected-root-lock", - message: "Expected a root Quixos lock, found a lock fragment", - fileName, - line: 1, - column: 0, - }], + diagnostics: [ + { + phase: "validation", + code: "expected-root-lock", + message: "Expected a root Quixos lock, found a lock fragment", + fileName, + line: 1, + column: 0, + }, + ], }; } if (parsed.document.imports.length) { return { ok: false, - diagnostics: [{ - phase: "resolution", - code: "imports-require-file-resolution", - message: "This lock has imports and must be loaded from its repository rather than parsed as an isolated string", - fileName, - line: 1, - column: 0, - }], + diagnostics: [ + { + phase: "resolution", + code: "imports-require-file-resolution", + message: + "This lock has imports and must be loaded from its repository rather than parsed as an isolated string", + fileName, + line: 1, + column: 0, + }, + ], }; } return { @@ -407,44 +379,23 @@ const sourceLines = (source: GitSource, indentation: string): string[] => [ const quixosSourceLines = (source: QuixosSource, indentation: string): string[] => [ `${indentation}repository ${quoted(source.repository)};`, - ...(source.policy - ? [ - `${indentation}policy ${source.policy};`, - `${indentation}ref ${quoted(source.ref)};`, - ] - : []), + ...(source.policy ? [`${indentation}policy ${source.policy};`, `${indentation}ref ${quoted(source.ref)};`] : []), `${indentation}commit ${quoted(source.commit.toLowerCase())};`, ]; export const formatQuixosLock = (lock: QuixosRepositoryLock): string => { - const lines = [ - "quixos-lock version 1 {", - " quixos source {", - ...quixosSourceLines(lock.quixos, " "), - " }", - ]; + const lines = ["quixos-lock version 1 {", " quixos source {", ...quixosSourceLines(lock.quixos, " "), " }"]; for (const resource of lock.resources) { - lines.push( - "", - ` ${resource.kind} ${resource.binding} source {`, - ...sourceLines(resource.source, " "), - " }", - ); + lines.push("", ` ${resource.kind} ${resource.binding} source {`, ...sourceLines(resource.source, " "), " }"); } lines.push("}", ""); return lines.join("\n"); }; export const formatQuixosLockDocument = (document: QuixosLockDocument): string => { - const lines = [ - `quixos-lock${document.kind === "fragment" ? " fragment" : ""} version 1 {`, - ]; + const lines = [`quixos-lock${document.kind === "fragment" ? " fragment" : ""} version 1 {`]; if (document.kind === "root") { - lines.push( - " quixos source {", - ...quixosSourceLines(document.quixos, " "), - " }", - ); + lines.push(" quixos source {", ...quixosSourceLines(document.quixos, " "), " }"); } for (const importPath of document.imports) { if (lines.length > 1) lines.push(""); @@ -452,11 +403,7 @@ export const formatQuixosLockDocument = (document: QuixosLockDocument): string = } for (const resource of document.resources) { if (lines.length > 1) lines.push(""); - lines.push( - ` ${resource.kind} ${resource.binding} source {`, - ...sourceLines(resource.source, " "), - " }", - ); + lines.push(` ${resource.kind} ${resource.binding} source {`, ...sourceLines(resource.source, " "), " }"); } lines.push("}", ""); return lines.join("\n"); diff --git a/src/resource-lock/types.ts b/src/resource-lock/types.ts index c073879..a7cf809 100644 --- a/src/resource-lock/types.ts +++ b/src/resource-lock/types.ts @@ -9,13 +9,17 @@ export type QuixosSourcePolicy = "pinned" | "track-release" | "track-development // Resource repositories only need the exact Quixos commit they were authored // against. A workspace root additionally declares how a runtime may advance // that exact baseline. -export type QuixosSource = GitSource & ({ - policy: QuixosSourcePolicy; - ref: string; -} | { - policy?: undefined; - ref?: undefined; -}); +export type QuixosSource = GitSource & + ( + | { + policy: QuixosSourcePolicy; + ref: string; + } + | { + policy?: undefined; + ref?: undefined; + } + ); export type LockedResourceKind = "interface" | "package"; @@ -40,9 +44,7 @@ export type QuixosLockFragmentDocument = { resources: LockedResource[]; }; -export type QuixosLockDocument = - | QuixosLockRootDocument - | QuixosLockFragmentDocument; +export type QuixosLockDocument = QuixosLockRootDocument | QuixosLockFragmentDocument; export type QuixosRepositoryLock = { formatVersion: 1; @@ -60,8 +62,7 @@ export type NixGitInput = { export const RETENTION_TAG_PREFIX = "refs/tags/quixos-reachability/"; -export const retentionTagForCommit = (commit: string): string => - `${RETENTION_TAG_PREFIX}${commit.toLowerCase()}`; +export const retentionTagForCommit = (commit: string): string => `${RETENTION_TAG_PREFIX}${commit.toLowerCase()}`; export const nixGitInput = (source: GitSource): NixGitInput => ({ type: "git", diff --git a/test/authoring-converge.test.ts b/test/authoring-converge.test.ts index 8f0b8fa..511c6c1 100644 --- a/test/authoring-converge.test.ts +++ b/test/authoring-converge.test.ts @@ -9,32 +9,55 @@ import { convergeAuthoring } from "../src/capability-language/authoring-converge import { loadQuixosLock } from "../src/resource-lock/index.js"; const execFile = promisify(callback); -test("source convergence propagates nested edits and unchanged snapshots reach a fixed point", async context => { +test("source convergence propagates nested edits and unchanged snapshots reach a fixed point", async (context) => { const temporary = await fs.mkdtemp(path.join(os.tmpdir(), "qx-converge-test-")); context.after(() => fs.rm(temporary, { recursive: true, force: true })); - const workbench = path.join(temporary, "workbench"), remotes = path.join(temporary, "remotes"); + const workbench = path.join(temporary, "workbench"), + remotes = path.join(temporary, "remotes"); await fs.mkdir(path.join(workbench, ".quixos"), { recursive: true }); await fs.mkdir(remotes); const origin = "https://convergence.example.test/"; - const previous = Object.fromEntries(["GIT_CONFIG_COUNT", "GIT_CONFIG_KEY_0", "GIT_CONFIG_VALUE_0"].map(key => [key, process.env[key]])); + const previous = Object.fromEntries( + ["GIT_CONFIG_COUNT", "GIT_CONFIG_KEY_0", "GIT_CONFIG_VALUE_0"].map((key) => [key, process.env[key]]), + ); process.env.GIT_CONFIG_COUNT = "1"; process.env.GIT_CONFIG_KEY_0 = `url.file://${remotes}/.insteadOf`; process.env.GIT_CONFIG_VALUE_0 = origin; - context.after(() => { for (const [key, value] of Object.entries(previous)) { if (value === undefined) delete process.env[key]; else process.env[key] = value; } }); + context.after(() => { + for (const [key, value] of Object.entries(previous)) { + if (value === undefined) delete process.env[key]; + else process.env[key] = value; + } + }); const resources = []; let dependency = ""; - for (const [directory, kind, name] of [["resources/Base", "interface", "Base"], ["resources/Consumer", "package", "Consumer"], ["root", "workspace", "Root"]]) { + for (const [directory, kind, name] of [ + ["resources/Base", "interface", "Base"], + ["resources/Consumer", "package", "Consumer"], + ["root", "workspace", "Root"], + ]) { const root = path.join(workbench, directory); await fs.mkdir(root, { recursive: true }); await execFile("jj", ["git", "init", "--colocate", root]); await execFile("git", ["init", "--bare", path.join(remotes, name)]); const repository = `${origin}${name}`; await execFile("git", ["-C", root, "remote", "add", "origin", repository]); - await fs.writeFile(path.join(root, "quixos.lock"), `quixos-lock version 1 { quixos source { repository "https://example.test/quixos"; commit "${"a".repeat(40)}"; } ${dependency} }`); + await fs.writeFile( + path.join(root, "quixos.lock"), + `quixos-lock version 1 { quixos source { repository "https://example.test/quixos"; commit "${"a".repeat(40)}"; } ${dependency} }`, + ); await fs.writeFile(path.join(root, `${kind}.qx`), "draft"); await execFile("jj", ["status"], { cwd: root }); - const commit = (await execFile("jj", ["--ignore-working-copy", "log", "--no-graph", "-r", "@", "-T", "commit_id"], { cwd: root })).stdout.trim(); - if (kind !== "workspace") resources.push({ directory, kind, resourceId: `${kind}:${name}`, source: { resolver: "git", repository, commit } }); + const commit = ( + await execFile("jj", ["--ignore-working-copy", "log", "--no-graph", "-r", "@", "-T", "commit_id"], { cwd: root }) + ).stdout.trim(); + if (kind !== "workspace") + resources.push({ + directory, + kind, + resourceId: `${kind}:${name}`, + source: { resolver: "git", repository, commit }, + }); dependency = `${kind} ${name} source { repository "${repository}"; commit "${commit}"; }`; } await fs.writeFile(path.join(workbench, ".quixos/resource-graph.json"), JSON.stringify({ resources })); @@ -48,7 +71,10 @@ test("source convergence propagates nested edits and unchanged snapshots reach a assert.notEqual(second.candidate?.commit, first.candidate?.commit); const consumer = await loadQuixosLock(path.join(workbench, "resources/Consumer/quixos.lock")); assert.ok(consumer.ok); - assert.equal(consumer.lock.resources[0].source.commit, second.retained.find(entry => entry.directory === "resources/Base")?.source.commit); + assert.equal( + consumer.lock.resources[0].source.commit, + second.retained.find((entry) => entry.directory === "resources/Base")?.source.commit, + ); const third = await convergeAuthoring(workbench); assert.deepEqual(third, second); const base = path.join(workbench, "resources/Base"); @@ -61,24 +87,30 @@ test("source convergence propagates nested edits and unchanged snapshots reach a const partial = await convergeAuthoring(workbench); assert.equal(partial.converged, false); const graph = JSON.parse(await fs.readFile(path.join(workbench, ".quixos/resource-graph.json"), "utf8")); - assert.equal(graph.resources[0].source.commit, partial.retained.find(entry => entry.directory === "resources/Base")?.source.commit); + assert.equal( + graph.resources[0].source.commit, + partial.retained.find((entry) => entry.directory === "resources/Base")?.source.commit, + ); await fs.writeFile(path.join(consumerRoot, "quixos.lock"), goodLock); const resumed = await convergeAuthoring(workbench); assert.deepEqual(resumed.worklist, []); assert.deepEqual(await convergeAuthoring(workbench), resumed); await execFile("git", ["-C", base, "remote", "set-url", "origin", origin + "Wrong"]); const mismatch = await convergeAuthoring(workbench); - assert.ok(mismatch.worklist.some(entry => entry.phase === "source" && /Origin differs/.test(entry.message))); + assert.ok(mismatch.worklist.some((entry) => entry.phase === "source" && /Origin differs/.test(entry.message))); await execFile("git", ["-C", base, "remote", "set-url", "origin", origin + "Base"]); await fs.rename(path.join(remotes, "Base"), path.join(remotes, "Base-offline")); const offline = await convergeAuthoring(workbench); assert.equal(offline.candidate, null); - assert.ok(offline.worklist.some(entry => entry.phase === "publication")); + assert.ok(offline.worklist.some((entry) => entry.phase === "publication")); await fs.rename(path.join(remotes, "Base-offline"), path.join(remotes, "Base")); assert.equal((await convergeAuthoring(workbench)).converged, true); - const consumerSource = resumed.retained.find(entry => entry.directory === "resources/Consumer")!.source; - await fs.writeFile(path.join(base, "quixos.lock"), `quixos-lock version 1 { quixos source { repository "https://example.test/quixos"; commit "${"a".repeat(40)}"; } package Consumer source { repository "${consumerSource.repository}"; commit "${consumerSource.commit}"; } }`); + const consumerSource = resumed.retained.find((entry) => entry.directory === "resources/Consumer")!.source; + await fs.writeFile( + path.join(base, "quixos.lock"), + `quixos-lock version 1 { quixos source { repository "https://example.test/quixos"; commit "${"a".repeat(40)}"; } package Consumer source { repository "${consumerSource.repository}"; commit "${consumerSource.commit}"; } }`, + ); const cycle = await convergeAuthoring(workbench); assert.equal(cycle.candidate, null); - assert.ok(cycle.worklist.some(entry => /Source dependency cycle/.test(entry.message))); + assert.ok(cycle.worklist.some((entry) => /Source dependency cycle/.test(entry.message))); }); diff --git a/test/authoring-inspect.test.ts b/test/authoring-inspect.test.ts index e9e4418..e4046fc 100644 --- a/test/authoring-inspect.test.ts +++ b/test/authoring-inspect.test.ts @@ -8,12 +8,15 @@ import { promisify } from "node:util"; import { inspectAuthoringRepository } from "../src/capability-language/authoring-inspect.js"; const execFile = promisify(callback); -test("inspection keeps current files and labels historical recovery without granting verification", async context => { +test("inspection keeps current files and labels historical recovery without granting verification", async (context) => { const root = await mkdtemp(path.join(os.tmpdir(), "qx-inspection-test-")); context.after(() => rm(root, { recursive: true, force: true })); const git = (...args: string[]) => execFile("git", ["-C", root, ...args]); await git("init"); - await writeFile(path.join(root, "interface.qx"), 'interface Example id "interface:example" revision "interface:example@1" {}'); + await writeFile( + path.join(root, "interface.qx"), + 'interface Example id "interface:example" revision "interface:example@1" {}', + ); await git("add", "."); await git("-c", "user.name=Test", "-c", "user.email=test@example.test", "commit", "-m", "contract"); const commit = (await git("rev-parse", "HEAD")).stdout.trim(); @@ -30,7 +33,7 @@ test("inspection keeps current files and labels historical recovery without gran assert.match(inspected.files[1].declarations[0].source, /New/); assert.equal((await git("rev-parse", "HEAD")).stdout.trim(), commit); await symlink(path.join(root, "interface.qx"), path.join(root, "linked.qx")); - const linked = (await inspectAuthoringRepository(root)).files.find(file => file.file === "linked.qx")!; + const linked = (await inspectAuthoringRepository(root)).files.find((file) => file.file === "linked.qx")!; assert.equal(linked.status, "unavailable"); assert.deepEqual(linked.declarations, []); assert.match(JSON.stringify(linked.currentErrors), /ordinary files/); diff --git a/test/authoring-worklist.test.ts b/test/authoring-worklist.test.ts index 659db7a..4967c9f 100644 --- a/test/authoring-worklist.test.ts +++ b/test/authoring-worklist.test.ts @@ -3,43 +3,81 @@ import assert from "node:assert/strict"; import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import {execFile as callback} from "node:child_process"; -import {promisify} from "node:util"; -import {authoringWorklist} from "../src/capability-language/authoring-worklist.js"; -import {checkRecordName} from "../src/capability-language/authoring-check.js"; -import {checkerIdentity, snapshotCommit} from "../src/capability-language/checked-build.js"; +import { execFile as callback } from "node:child_process"; +import { promisify } from "node:util"; +import { authoringWorklist } from "../src/capability-language/authoring-worklist.js"; +import { checkRecordName } from "../src/capability-language/authoring-check.js"; +import { checkerIdentity, snapshotCommit } from "../src/capability-language/checked-build.js"; const execFile = promisify(callback); -test("worklist grows and clears from current source, dependency and checker observations", async context => { +test("worklist grows and clears from current source, dependency and checker observations", async (context) => { const workbench = await fs.mkdtemp(path.join(os.tmpdir(), "qx-worklist-test-")); - context.after(() => fs.rm(workbench, {recursive: true, force: true})); - const root = path.join(workbench, "root"), provider = path.join(workbench, "resources/Base"); - await fs.mkdir(root); await fs.mkdir(provider, {recursive: true}); - await fs.mkdir(path.join(workbench, ".quixos/checks"), {recursive: true}); + context.after(() => fs.rm(workbench, { recursive: true, force: true })); + const root = path.join(workbench, "root"), + provider = path.join(workbench, "resources/Base"); + await fs.mkdir(root); + await fs.mkdir(provider, { recursive: true }); + await fs.mkdir(path.join(workbench, ".quixos/checks"), { recursive: true }); const header = `quixos-lock version 1 { quixos source { repository "https://example.test/quixos"; commit "${"a".repeat(40)}"; }`; for (const directory of [root, provider]) await execFile("jj", ["git", "init", "--colocate", directory]); - await fs.writeFile(path.join(provider, "interface.qx"), 'interface Base id "interface:base" revision "interface:base@1" {}'); + await fs.writeFile( + path.join(provider, "interface.qx"), + 'interface Base id "interface:base" revision "interface:base@1" {}', + ); await fs.writeFile(path.join(provider, "quixos.lock"), `${header} }`); const baseCommit = await snapshotCommit(provider); - await fs.writeFile(path.join(root, "workspace.qx"), `workspace Test id "workspace:test" revision "workspace:test@1" commit "${"b".repeat(40)}" { import interface Base; }`); - await fs.writeFile(path.join(root, "quixos.lock"), `${header} interface Base source { repository "https://example.test/base"; commit "${baseCommit}"; } }`); + await fs.writeFile( + path.join(root, "workspace.qx"), + `workspace Test id "workspace:test" revision "workspace:test@1" commit "${"b".repeat(40)}" { import interface Base; }`, + ); + await fs.writeFile( + path.join(root, "quixos.lock"), + `${header} interface Base source { repository "https://example.test/base"; commit "${baseCommit}"; } }`, + ); const rootCommit = await snapshotCommit(root); - await fs.writeFile(path.join(workbench, ".quixos/resource-graph.json"), JSON.stringify({resources: [ - {kind: "interface", directory: provider, source: {resolver: "git", repository: "https://example.test/base", commit: baseCommit}}, - ]})); - assert.equal((await authoringWorklist(workbench)).worklist.filter(entry => entry.phase === "unchecked").length, 2); - const remember = (directory: string, commit: string, checker = checkerIdentity()) => fs.writeFile( - path.join(workbench, ".quixos/checks", checkRecordName(directory)), JSON.stringify({commit, checker, phase: "checked", blockers: []})); - await remember("root", rootCommit); await remember("resources/Base", baseCommit); + await fs.writeFile( + path.join(workbench, ".quixos/resource-graph.json"), + JSON.stringify({ + resources: [ + { + kind: "interface", + directory: provider, + source: { resolver: "git", repository: "https://example.test/base", commit: baseCommit }, + }, + ], + }), + ); + assert.equal((await authoringWorklist(workbench)).worklist.filter((entry) => entry.phase === "unchecked").length, 2); + const remember = (directory: string, commit: string, checker = checkerIdentity()) => + fs.writeFile( + path.join(workbench, ".quixos/checks", checkRecordName(directory)), + JSON.stringify({ commit, checker, phase: "checked", blockers: [] }), + ); + await remember("root", rootCommit); + await remember("resources/Base", baseCommit); assert.deepEqual((await authoringWorklist(workbench)).worklist, []); await fs.writeFile(path.join(provider, "interface.qx"), "interface broken {{{"); const broken = await authoringWorklist(workbench); - assert.ok(broken.worklist.some(entry => entry.directory === "resources/Base" && entry.phase === "syntax" && /historical/.test(entry.message))); - assert.ok(broken.worklist.some(entry => entry.directory === "root" && entry.phase === "dependency")); - await fs.writeFile(path.join(provider, "interface.qx"), 'interface Base id "interface:base" revision "interface:base@1" {}'); + assert.ok( + broken.worklist.some( + (entry) => entry.directory === "resources/Base" && entry.phase === "syntax" && /historical/.test(entry.message), + ), + ); + assert.ok(broken.worklist.some((entry) => entry.directory === "root" && entry.phase === "dependency")); + await fs.writeFile( + path.join(provider, "interface.qx"), + 'interface Base id "interface:base" revision "interface:base@1" {}', + ); assert.deepEqual((await authoringWorklist(workbench)).worklist, []); await remember("resources/Base", baseCommit, "old-checker"); - assert.ok((await authoringWorklist(workbench)).worklist.some(entry => /checker changed/.test(entry.message))); - await fs.writeFile(path.join(workbench, ".quixos/checks", checkRecordName("resources/Base")), JSON.stringify({phase: "publication", blockers: ["source retention unavailable"]})); - assert.ok((await authoringWorklist(workbench)).worklist.some(entry => entry.phase === "publication" && /source retention/.test(entry.message))); + assert.ok((await authoringWorklist(workbench)).worklist.some((entry) => /checker changed/.test(entry.message))); + await fs.writeFile( + path.join(workbench, ".quixos/checks", checkRecordName("resources/Base")), + JSON.stringify({ phase: "publication", blockers: ["source retention unavailable"] }), + ); + assert.ok( + (await authoringWorklist(workbench)).worklist.some( + (entry) => entry.phase === "publication" && /source retention/.test(entry.message), + ), + ); }); diff --git a/test/bindings.test.ts b/test/bindings.test.ts index 60b7339..4dccdaa 100644 --- a/test/bindings.test.ts +++ b/test/bindings.test.ts @@ -4,13 +4,18 @@ import { compileCapabilityResourceSource } from "../src/capability-language/pars import { generateTypeScriptBindings, type BindingSchema } from "../src/bindings/index.js"; const source = { repository: "https://example.test/p.git", commit: "1".repeat(40) }; const schemaFor = (body: string): BindingSchema => { - const compiled = compileCapabilityResourceSource(`external atom Thing id "thing"; package Demo id "demo" revision "demo@1" { ${body} }`, { source }); + const compiled = compileCapabilityResourceSource( + `external atom Thing id "thing"; package Demo id "demo" revision "demo@1" { ${body} }`, + { source }, + ); assert.equal(compiled.ok, true, JSON.stringify(compiled.diagnostics)); if (!compiled.ok || compiled.resource.kind !== "package") throw new Error("expected package"); return { format: "quixos-bindings", version: 1, interfaces: [], packages: [compiled.resource.revision] }; }; test("generator uses exact IDs, restricted ports, nominal references, and lossless scalar types", () => { - const schema = schemaFor('operation run id "run-id" : list -> optional> mode call receiver atom Thing requires { state payload id "payload-id" : bytes [read]; };'); + const schema = schemaFor( + 'operation run id "run-id" : list -> optional> mode call receiver atom Thing requires { state payload id "payload-id" : bytes [read]; };', + ); const generated = generateTypeScriptBindings(schema, "demo@1"); assert.match(generated, /Array/); assert.match(generated, /Promise/); @@ -22,9 +27,18 @@ test("generator uses exact IDs, restricted ports, nominal references, and lossle test("missing external types and constructor signatures fail generation", () => { const schema = schemaFor('function run id "run" : unit -> message "example.Payload";'); assert.throws(() => generateTypeScriptBindings(schema, "demo@1"), /Missing TypeScript message binding/); - assert.match(generateTypeScriptBindings(schema, "demo@1", { messages: { "example.Payload": { module: "./payload.js", export: "payload" } } }), /BindingValue/); - const constructor = schemaFor('function run id "run" : unit -> unit requires { constructor thing id "ctor" : Thing; };'); + assert.match( + generateTypeScriptBindings(schema, "demo@1", { + messages: { "example.Payload": { module: "./payload.js", export: "payload" } }, + }), + /BindingValue/, + ); + const constructor = schemaFor( + 'function run id "run" : unit -> unit requires { constructor thing id "ctor" : Thing; };', + ); assert.throws(() => generateTypeScriptBindings(constructor, "demo@1"), /explicit input contract/); - const typed = schemaFor('function run id "run" : unit -> unit requires { constructor thing id "ctor" : Thing input string; };'); + const typed = schemaFor( + 'function run id "run" : unit -> unit requires { constructor thing id "ctor" : Thing input string; };', + ); assert.match(generateTypeScriptBindings(typed, "demo@1"), /construct.*input: string/); }); diff --git a/test/bundle-policy.test.ts b/test/bundle-policy.test.ts index b328036..306d5aa 100644 --- a/test/bundle-policy.test.ts +++ b/test/bundle-policy.test.ts @@ -1,21 +1,36 @@ import test from "node:test"; import assert from "node:assert/strict"; -import {bundlePolicyErrors} from "../src/bindings/bundle-policy.js"; -import {addImplementation} from "../src/capability-language/implementation-edit.js"; +import { bundlePolicyErrors } from "../src/bindings/bundle-policy.js"; +import { addImplementation } from "../src/capability-language/implementation-edit.js"; test("bundled source policy rejects location and dynamic-loading assumptions, not static assets or runtime I/O", () => { - for (const code of ['new URL("../x", import.meta.url)', '__dirname', '__filename', 'import(name)', 'require(name)', 'eval(code)', 'new Function(code)']) + for (const code of [ + 'new URL("../x", import.meta.url)', + "__dirname", + "__filename", + "import(name)", + "require(name)", + "eval(code)", + "new Function(code)", + ]) assert.ok(bundlePolicyErrors(code, "source.ts").length, code); - assert.deepEqual(bundlePolicyErrors('import source from "./component.js?browser-source"; import fs from "node:fs"; fs.readFile(userSelectedPath);', "source.ts"), []); + assert.deepEqual( + bundlePolicyErrors( + 'import source from "./component.js?browser-source"; import fs from "node:fs"; fs.readFile(userSelectedPath);', + "source.ts", + ), + [], + ); assert.deepEqual(bundlePolicyErrors('// import.meta.url\nconst text = "__dirname";', "source.ts"), []); }); test("imperative handler insertion preserves arbitrary existing code and rejects ambiguous targets", () => { - const original = 'const keep = "createRuntime({fake:1})";\nservePackageRuntime(createRuntime({ existing: customHandler }));\n'; + const original = + 'const keep = "createRuntime({fake:1})";\nservePackageRuntime(createRuntime({ existing: customHandler }));\n'; const edited = addImplementation(original, "createRuntime", "newHandler", "./impl/new.js"); assert.match(edited, /existing: customHandler/); assert.match(edited, /const keep =/); assert.match(edited, /"newHandler": qxImplementation/); assert.throws(() => addImplementation(original, "createRuntime", "existing", "./x.js"), /already exists/); - assert.throws(() => addImplementation('createRuntime(one);', "createRuntime", "x", "./x.js"), /Cannot safely/); + assert.throws(() => addImplementation("createRuntime(one);", "createRuntime", "x", "./x.js"), /Cannot safely/); }); diff --git a/test/candidate-check.test.ts b/test/candidate-check.test.ts index 757d46c..1e0d643 100644 --- a/test/candidate-check.test.ts +++ b/test/candidate-check.test.ts @@ -35,16 +35,23 @@ test("candidate snapshots include dirty and new files without changing Git histo test("candidate check produces explicitly non-activation evidence and never overwrites a report", async (context) => { const directory = await fs.mkdtemp(path.join(os.tmpdir(), "qx-check-test-")); context.after(() => fs.rm(directory, { recursive: true, force: true })); - const root = path.join(directory, "source"), output = path.join(directory, "check"); + const root = path.join(directory, "source"), + output = path.join(directory, "check"); await fs.mkdir(root); await execFile("jj", ["git", "init", "--colocate", root]); - await fs.writeFile(path.join(root, "quixos.lock"), `quixos-lock version 1 { quixos source { repository "https://example.test/quixos.git"; commit "${"a".repeat(40)}"; } }`); - await fs.writeFile(path.join(root, "workspace.qx"), `workspace Test id "workspace:test" revision "workspace:test@1" commit "${"b".repeat(40)}" { atom Subject id "atom:subject"; }`); - const result = await checkWorkspaceCandidate({root, output}); + await fs.writeFile( + path.join(root, "quixos.lock"), + `quixos-lock version 1 { quixos source { repository "https://example.test/quixos.git"; commit "${"a".repeat(40)}"; } }`, + ); + await fs.writeFile( + path.join(root, "workspace.qx"), + `workspace Test id "workspace:test" revision "workspace:test@1" commit "${"b".repeat(40)}" { atom Subject id "atom:subject"; }`, + ); + const result = await checkWorkspaceCandidate({ root, output }); assert.equal(result.candidateOnly, true); assert.equal(result.activationEvidence, false); assert.deepEqual(result.blockers, []); const report = await fs.readFile(path.join(output, "report.json")); - await assert.rejects(checkWorkspaceCandidate({root, output}), /EEXIST/); + await assert.rejects(checkWorkspaceCandidate({ root, output }), /EEXIST/); assert.deepEqual(await fs.readFile(path.join(output, "report.json")), report); }); diff --git a/test/capability-assembly.test.ts b/test/capability-assembly.test.ts index 200ad38..84f72b5 100644 --- a/test/capability-assembly.test.ts +++ b/test/capability-assembly.test.ts @@ -3,10 +3,7 @@ import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import test from "node:test"; -import { - compileCapabilityResourceRepository, - compileWorkspaceRepository, -} from "../src/capability-language/index.js"; +import { compileCapabilityResourceRepository, compileWorkspaceRepository } from "../src/capability-language/index.js"; const quixosCommit = "1".repeat(40); const namedCommit = "2".repeat(40); @@ -28,31 +25,46 @@ test("workspace assembly resolves resource-owned dependencies recursively", asyn const named = path.join(directory, "named"); const runtime = path.join(directory, "runtime"); await Promise.all([mkdir(root), mkdir(named), mkdir(runtime)]); - await writeFile(path.join(root, "quixos.lock"), lock(`package Runtime source { + await writeFile( + path.join(root, "quixos.lock"), + lock(`package Runtime source { repository "https://example.test/package-runtime.git"; commit "${packageCommit}"; - }`)); - await writeFile(path.join(root, "workspace.qx"), `workspace Test id "workspace:test" revision "workspace:test@1" commit "${"4".repeat(40)}" { + }`), + ); + await writeFile( + path.join(root, "workspace.qx"), + `workspace Test id "workspace:test" revision "workspace:test@1" commit "${"4".repeat(40)}" { atom Subject id "atom:subject"; import package Runtime; } -`); +`, + ); await writeFile(path.join(named, "quixos.lock"), lock()); - await writeFile(path.join(named, "interface.qx"), `interface Named id "interface:named" revision "interface:named@1" { + await writeFile( + path.join(named, "interface.qx"), + `interface Named id "interface:named" revision "interface:named@1" { value name id "member:named:name" : string { get id "operation:named:name:get"; } } -`); - await writeFile(path.join(runtime, "quixos.lock"), lock(`interface Named source { +`, + ); + await writeFile( + path.join(runtime, "quixos.lock"), + lock(`interface Named source { repository "https://example.test/interface-named.git"; commit "${namedCommit}"; - }`)); - await writeFile(path.join(runtime, "package.qx"), `import interface Named; + }`), + ); + await writeFile( + path.join(runtime, "package.qx"), + `import interface Named; package Runtime id "package:runtime" revision "package:runtime@1" { function describe id "export:runtime:describe" : interface-ref -> string; } -`); +`, + ); const directories = new Map([ [`interface\0https://example.test/interface-named.git\0${namedCommit}`, named], @@ -100,15 +112,21 @@ package Runtime id "package:runtime" revision "package:runtime@1" { test("resource repositories cannot own workspace Quixos selection policy", async (context) => { const directory = await mkdtemp(path.join(os.tmpdir(), "quixos-resource-policy-")); context.after(() => rm(directory, { recursive: true, force: true })); - await writeFile(path.join(directory, "quixos.lock"), `quixos-lock version 1 { + await writeFile( + path.join(directory, "quixos.lock"), + `quixos-lock version 1 { quixos source { repository "https://example.test/quixos.git"; policy track-development; ref "dev/alice/main"; commit "${quixosCommit}"; } - }`); - await writeFile(path.join(directory, "package.qx"), `package Runtime id "package:runtime" revision "package:runtime@1" { }\n`); + }`, + ); + await writeFile( + path.join(directory, "package.qx"), + `package Runtime id "package:runtime" revision "package:runtime@1" { }\n`, + ); await assert.rejects( compileCapabilityResourceRepository({ @@ -131,19 +149,28 @@ test("resource manifests cannot hide lock dependencies", async (context) => { const root = path.join(directory, "root"); const runtime = path.join(directory, "runtime"); await Promise.all([mkdir(root), mkdir(runtime)]); - await writeFile(path.join(root, "quixos.lock"), lock(`package Runtime source { + await writeFile( + path.join(root, "quixos.lock"), + lock(`package Runtime source { repository "https://example.test/package-runtime.git"; commit "${packageCommit}"; - }`)); - await writeFile(path.join(root, "workspace.qx"), `workspace Test id "workspace:test" revision "workspace:test@1" commit "${"4".repeat(40)}" { + }`), + ); + await writeFile( + path.join(root, "workspace.qx"), + `workspace Test id "workspace:test" revision "workspace:test@1" commit "${"4".repeat(40)}" { atom Subject id "atom:subject"; import package Runtime; } -`); +`, + ); await writeFile(path.join(runtime, "quixos.lock"), lock()); - await writeFile(path.join(runtime, "package.qx"), `import interface Hidden; + await writeFile( + path.join(runtime, "package.qx"), + `import interface Hidden; package Runtime id "package:runtime" revision "package:runtime@1" { } -`); +`, + ); await assert.rejects( compileWorkspaceRepository({ @@ -160,19 +187,28 @@ test("workspace assembly must satisfy nominal external interfaces", async (conte const root = path.join(directory, "root"); const runtime = path.join(directory, "runtime"); await Promise.all([mkdir(root), mkdir(runtime)]); - await writeFile(path.join(root, "quixos.lock"), lock(`package Runtime source { + await writeFile( + path.join(root, "quixos.lock"), + lock(`package Runtime source { repository "https://example.test/package-runtime.git"; commit "${packageCommit}"; - }`)); - await writeFile(path.join(root, "workspace.qx"), `workspace Test id "workspace:test" revision "workspace:test@1" commit "${"4".repeat(40)}" { + }`), + ); + await writeFile( + path.join(root, "workspace.qx"), + `workspace Test id "workspace:test" revision "workspace:test@1" commit "${"4".repeat(40)}" { atom Subject id "atom:subject"; import package Runtime; } -`); +`, + ); await writeFile(path.join(runtime, "quixos.lock"), lock()); - await writeFile(path.join(runtime, "package.qx"), `external interface Named revision "interface:named@1"; + await writeFile( + path.join(runtime, "package.qx"), + `external interface Named revision "interface:named@1"; package Runtime id "package:runtime" revision "package:runtime@1" { } -`); +`, + ); await assert.rejects( compileWorkspaceRepository({ diff --git a/test/capability-cli.test.ts b/test/capability-cli.test.ts index b239f96..73fa856 100644 --- a/test/capability-cli.test.ts +++ b/test/capability-cli.test.ts @@ -5,14 +5,12 @@ import { test } from "node:test"; const cli = resolve(process.cwd(), "dist/src/capability-language/cli.js"); -const runResourceCheck = (source: string) => spawnSync( - process.execPath, - [cli, "--resource", "--check", "-"], - { input: source, encoding: "utf8" }, -); +const runResourceCheck = (source: string) => + spawnSync(process.execPath, [cli, "--resource", "--check", "-"], { input: source, encoding: "utf8" }); test("capability CLI checks a standalone interface resource", () => { - const result = runResourceCheck(`interface WeatherBase id "interface:weather-base" revision "interface:weather-base@1" { + const result = + runResourceCheck(`interface WeatherBase id "interface:weather-base" revision "interface:weather-base@1" { value temperature id "member:weather:temperature" : double { get id "operation:weather:temperature:get"; } @@ -22,7 +20,8 @@ test("capability CLI checks a standalone interface resource", () => { }); test("capability CLI reports invalid standalone interface members", () => { - const result = runResourceCheck(`interface WeatherBase id "interface:weather-base" revision "interface:weather-base@1" { + const result = + runResourceCheck(`interface WeatherBase id "interface:weather-base" revision "interface:weather-base@1" { value temperature : definitely-not-a-type; }\n`); assert.notEqual(result.status, 0); diff --git a/test/capability-language.test.ts b/test/capability-language.test.ts index 6ce8e9b..9a598da 100644 --- a/test/capability-language.test.ts +++ b/test/capability-language.test.ts @@ -2,10 +2,7 @@ import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { test } from "node:test"; -import { - compileCapabilityResourceSource, - compileCapabilitySource, -} from "../src/capability-language/index.js"; +import { compileCapabilityResourceSource, compileCapabilitySource } from "../src/capability-language/index.js"; import { capabilityFixtureSource, capabilityResourceSources, @@ -14,91 +11,76 @@ import { } from "./fixtures/capability-model.js"; test("RPC record fields retain declared reference targets and reject duplicate fields", () => { - const compile = (fields: string) => compileCapabilityResourceSource(` + const compile = (fields: string) => + compileCapabilityResourceSource( + ` external atom Board id "atom:board"; package Runtime id "package:runtime" revision "package:runtime@1" { function move id "export:move" : record { ${fields} } -> unit; -}`, {source: {repository: "https://example.test/runtime.git", commit: "a".repeat(40)}}); +}`, + { source: { repository: "https://example.test/runtime.git", commit: "a".repeat(40) } }, + ); const result = compile("board: atom-ref; position: record { x: double; y: double; }; note: optional;"); assert.equal(result.ok, true, JSON.stringify(result.diagnostics)); assert.equal(compile("board: atom-ref; board: string;").ok, false); }); const compileWebStudioFixture = (packageTransform = (source: string) => source) => { - const fixture = (name: string) => readFileSync( - resolve(process.cwd(), "test/fixtures", name), - "utf8", - ); - const react = compileCapabilityResourceSource( - fixture("react-component.interface.qx"), - { - source: { - repository: "https://repos.quixos.org/org-quixos-web-studio/interface-react-component.git", - commit: "2".repeat(40), - }, + const fixture = (name: string) => readFileSync(resolve(process.cwd(), "test/fixtures", name), "utf8"); + const react = compileCapabilityResourceSource(fixture("react-component.interface.qx"), { + source: { + repository: "https://repos.quixos.org/org-quixos-web-studio/interface-react-component.git", + commit: "2".repeat(40), }, - ); + }); if (!react.ok || react.resource.kind !== "interface") { throw new Error("ReactComponent fixture did not compile"); } - const named = compileCapabilityResourceSource( - fixture("named.interface.qx"), - { - source: { - repository: "https://repos.quixos.org/quixos-test/interface-named.git", - commit: "5".repeat(40), - }, + const named = compileCapabilityResourceSource(fixture("named.interface.qx"), { + source: { + repository: "https://repos.quixos.org/quixos-test/interface-named.git", + commit: "5".repeat(40), }, - ); + }); if (!named.ok || named.resource.kind !== "interface") { throw new Error("Named fixture did not compile"); } - const has = compileCapabilityResourceSource( - fixture("has-react-component.interface.qx"), - { - source: { - repository: "https://repos.quixos.org/org-quixos-web-studio/interface-has-react-component.git", - commit: "3".repeat(40), - }, - environment: { - interfaces: new Map([["ReactComponent", react.resource.revision]]), - interfaceClosure: [react.resource.revision], - }, + const has = compileCapabilityResourceSource(fixture("has-react-component.interface.qx"), { + source: { + repository: "https://repos.quixos.org/org-quixos-web-studio/interface-has-react-component.git", + commit: "3".repeat(40), }, - ); + environment: { + interfaces: new Map([["ReactComponent", react.resource.revision]]), + interfaceClosure: [react.resource.revision], + }, + }); if (!has.ok || has.resource.kind !== "interface") { throw new Error("HasReactComponent fixture did not compile"); } - const component = compileCapabilityResourceSource( - packageTransform(fixture("component-runtime.package.qx")), - { - source: { - repository: "https://repos.quixos.org/quixos-test/package-component-runtime.git", - commit: "4".repeat(40), - }, - environment: { - interfaces: new Map([["Named", named.resource.revision]]), - interfaceClosure: [named.resource.revision], - }, + const component = compileCapabilityResourceSource(packageTransform(fixture("component-runtime.package.qx")), { + source: { + repository: "https://repos.quixos.org/quixos-test/package-component-runtime.git", + commit: "4".repeat(40), }, - ); + environment: { + interfaces: new Map([["Named", named.resource.revision]]), + interfaceClosure: [named.resource.revision], + }, + }); if (!component.ok || component.resource.kind !== "package") { throw new Error("ComponentRuntime fixture did not compile"); } - return compileCapabilitySource( - fixture("web-studio.capabilities.qx"), - "web-studio.capabilities.qx", - { - interfaces: new Map([ - ["Named", named.resource.revision], - ["ReactComponent", react.resource.revision], - ["HasReactComponent", has.resource.revision], - ]), - packages: new Map([["ComponentRuntime", component.resource.revision]]), - interfaceClosure: [named.resource.revision, react.resource.revision, has.resource.revision], - packageClosure: [component.resource.revision], - }, - ); + return compileCapabilitySource(fixture("web-studio.capabilities.qx"), "web-studio.capabilities.qx", { + interfaces: new Map([ + ["Named", named.resource.revision], + ["ReactComponent", react.resource.revision], + ["HasReactComponent", has.resource.revision], + ]), + packages: new Map([["ComponentRuntime", component.resource.revision]]), + interfaceClosure: [named.resource.revision, react.resource.revision, has.resource.revision], + packageClosure: [component.resource.revision], + }); }; test("ANTLR parses and validates a complete capability workspace", () => { @@ -128,7 +110,7 @@ test("the v1 language has no implicit relationship materialization rule", () => const result = compileCapabilityFixture({ workspace: capabilityFixtureSource.replace( /\n}\s*$/, - '\n materialize ProjectOwner.owner if absent with Person;\n}\n', + "\n materialize ProjectOwner.owner if absent with Person;\n}\n", ), }); assert.equal(result.ok, false); @@ -140,10 +122,7 @@ test("forward declarations make source order irrelevant", () => { const source = capabilityFixtureSource .replace(/ atom Project[^\n]*\n/, "") .replace(/ atom Person[^\n]*\n/, "") - .replace( - /\n}\s*$/, - '\n atom Project id "atom:project";\n atom Person id "atom:person";\n}\n', - ); + .replace(/\n}\s*$/, '\n atom Project id "atom:project";\n atom Person id "atom:person";\n}\n'); assert.equal(compileCapabilityFixture({ workspace: source }).ok, true); }); @@ -153,18 +132,17 @@ test("interfaces can declare ordinary call operations", () => { "bind summarize.call to package TodoRuntime.summaryGet", ); const summary = capabilityResourceSources.summary.replace( - " value summary id \"member:summary:summary\" : string {\n get id \"operation:summary:summary:get\";\n }", - " operation summarize id \"member:summary:summarize\" : string -> string {\n call id \"operation:summary:summarize\";\n }", + ' value summary id "member:summary:summary" : string {\n get id "operation:summary:summary:get";\n }', + ' operation summarize id "member:summary:summarize" : string -> string {\n call id "operation:summary:summarize";\n }', ); const todo = capabilityResourceSources.todo.replace( - "operation summaryGet id \"export:todo-runtime:summary-get\" : unit -> string", - "operation summaryGet id \"export:todo-runtime:summary-get\" : string -> string", + 'operation summaryGet id "export:todo-runtime:summary-get" : unit -> string', + 'operation summaryGet id "export:todo-runtime:summary-get" : string -> string', ); const result = compileCapabilityFixture({ workspace, summary, todo }); assert.equal(result.ok, true); if (!result.ok) return; - const member = result.workspace.interfaceImports - .find((entry) => entry.displayName === "Summary")?.members[0]; + const member = result.workspace.interfaceImports.find((entry) => entry.displayName === "Summary")?.members[0]; assert.equal(member?.kind, "operation"); }); @@ -178,9 +156,7 @@ test("state defaults accept recursive JSON values", () => { const result = compileCapabilityFixture({ workspace: source }); assert.equal(result.ok, true); if (!result.ok) return; - const metadata = result.workspace.sharedAttachments.find( - (entry) => entry.id === "slot:project:metadata", - ); + const metadata = result.workspace.sharedAttachments.find((entry) => entry.id === "slot:project:metadata"); assert.equal(metadata?.kind, "state"); if (metadata?.kind !== "state") return; assert.deepEqual(metadata.defaultValue, { @@ -197,25 +173,20 @@ test("syntax errors retain source locations", () => { }); assert.equal(result.ok, false); if (result.ok) return; - assert.ok(result.diagnostics.some((entry) => - entry.phase === "syntax" && entry.line > 0 - )); + assert.ok(result.diagnostics.some((entry) => entry.phase === "syntax" && entry.line > 0)); }); test("unknown authoring names are lowering errors", () => { const result = compileCapabilityFixture({ - workspace: capabilityFixtureSource.replace( - "to state ProjectTitle.read", - "to state NotAState.read", - ), + workspace: capabilityFixtureSource.replace("to state ProjectTitle.read", "to state NotAState.read"), }); assert.equal(result.ok, false); if (result.ok) return; - assert.ok(result.diagnostics.some((entry) => - entry.phase === "lowering" && - entry.code === "unknown-symbol" && - entry.message.includes("NotAState") - )); + assert.ok( + result.diagnostics.some( + (entry) => entry.phase === "lowering" && entry.code === "unknown-symbol" && entry.message.includes("NotAState"), + ), + ); }); test("well-formed but invalid programs report semantic paths", () => { @@ -227,9 +198,7 @@ test("well-formed but invalid programs report semantic paths", () => { }); assert.equal(result.ok, false); if (result.ok) return; - const diagnostic = result.diagnostics.find( - (entry) => entry.code === "invalid-state-binding", - ); + const diagnostic = result.diagnostics.find((entry) => entry.code === "invalid-state-binding"); assert.ok(diagnostic); assert.equal(diagnostic.phase, "validation"); assert.ok(diagnostic.path?.includes("operationBindings")); @@ -240,20 +209,21 @@ test("Web Studio sidecars declare lazy materialization and checked cross-object assert.equal(result.ok, true); if (!result.ok) return; - const host = result.workspace.conformances.find((entry) => - entry.atomId === "atom:project" && - entry.interfaceRevisionId === "interface:org.quixos.web-studio.has-react-component@1" + const host = result.workspace.conformances.find( + (entry) => + entry.atomId === "atom:project" && + entry.interfaceRevisionId === "interface:org.quixos.web-studio.has-react-component@1", ); - assert.deepEqual(host?.relationshipMaterializations, [{ - memberId: "member:org.quixos.web-studio.has-react-component:component", - constructorAtomId: "atom:project-component", - edgeTypeId: "edge:project:component", - constructedProjectionId: "projection:component:subject", - }]); + assert.deepEqual(host?.relationshipMaterializations, [ + { + memberId: "member:org.quixos.web-studio.has-react-component:component", + constructorAtomId: "atom:project-component", + edgeTypeId: "edge:project:component", + constructedProjectionId: "projection:component:subject", + }, + ]); - const component = result.workspace.conformances.find((entry) => - entry.atomId === "atom:project-component" - ); + const component = result.workspace.conformances.find((entry) => entry.atomId === "atom:project-component"); const binding = component?.operationBindings[0]?.binding; assert.equal(binding?.kind, "package"); if (binding?.kind !== "package") return; @@ -268,35 +238,40 @@ test("Web Studio sidecars declare lazy materialization and checked cross-object }); test("relationship materializers require a constructor from the host atom", () => { - const result = compileWebStudioFixture((source) => source.replace( - "constructs ProjectComponent : atom-ref;", - "constructs ProjectComponent : unit;", - )); + const result = compileWebStudioFixture((source) => + source.replace("constructs ProjectComponent : atom-ref;", "constructs ProjectComponent : unit;"), + ); assert.equal(result.ok, false); if (result.ok) return; - assert.ok(result.diagnostics.some((entry) => - entry.code === "invalid-relationship-materialization" && - entry.message.includes("must accept") - )); + assert.ok( + result.diagnostics.some( + (entry) => entry.code === "invalid-relationship-materialization" && entry.message.includes("must accept"), + ), + ); }); test("callable interface ports require a full source dependency, not a nominal reference", () => { - const result = compileCapabilityResourceSource(` + const result = compileCapabilityResourceSource( + ` external interface Named revision "interface:named@1"; package Runtime id "package:runtime" revision "package:runtime@1" { function readName id "export:runtime:read-name" : unit -> string requires { interface named id "port:runtime:named" : Named; }; } -`, { - source: { - repository: "https://repos.quixos.org/quixos-test/package-runtime.git", - commit: "6".repeat(40), +`, + { + source: { + repository: "https://repos.quixos.org/quixos-test/package-runtime.git", + commit: "6".repeat(40), + }, }, - }); + ); assert.equal(result.ok, false); if (result.ok) return; - assert.ok(result.diagnostics.some((entry) => - entry.code === "nominal-interface-port" && entry.message.includes("import interface Named") - )); + assert.ok( + result.diagnostics.some( + (entry) => entry.code === "nominal-interface-port" && entry.message.includes("import interface Named"), + ), + ); }); diff --git a/test/capability-model.test.ts b/test/capability-model.test.ts index 5dab7f0..28a3248 100644 --- a/test/capability-model.test.ts +++ b/test/capability-model.test.ts @@ -9,36 +9,36 @@ import { type CapabilityValidationIssueCode, type WorkspaceRevision, } from "../src/capability-model/index.js"; -import { - fixtureId, - makeValidCapabilityWorkspace, -} from "./fixtures/capability-model.js"; +import { fixtureId, makeValidCapabilityWorkspace } from "./fixtures/capability-model.js"; test("ordinary state rejects managed references even under list/optional wrappers", () => { const workspace = makeValidCapabilityWorkspace(); - const state = [...workspace.sharedAttachments, ...workspace.conformances.flatMap((entry) => entry.privateAttachments)].find((entry) => entry.kind === "state")!; + const state = [ + ...workspace.sharedAttachments, + ...workspace.conformances.flatMap((entry) => entry.privateAttachments), + ].find((entry) => entry.kind === "state")!; if (state.kind !== "state") throw new Error("fixture missing state"); - state.valueType = {kind: "list", value: {kind: "optional", value: {kind: "object-ref", expectation: {kind: "atom", atomId: state.attachedTo}}}}; + state.valueType = { + kind: "list", + value: { kind: "optional", value: { kind: "object-ref", expectation: { kind: "atom", atomId: state.attachedTo } } }, + }; assert.ok(validateWorkspaceRevision(workspace).some((entry) => entry.message.includes("graph relationships"))); }); -const expectIssue = ( - workspace: WorkspaceRevision, - code: CapabilityValidationIssueCode, -) => { +const expectIssue = (workspace: WorkspaceRevision, code: CapabilityValidationIssueCode) => { const issues = validateWorkspaceRevision(workspace); assert.ok( issues.some((entry) => entry.code === code), - `Expected ${code}, got:\n${issues - .map((entry) => `${entry.code} ${entry.path}: ${entry.message}`) - .join("\n")}`, + `Expected ${code}, got:\n${issues.map((entry) => `${entry.code} ${entry.path}: ${entry.message}`).join("\n")}`, ); assert.equal(compileWorkspaceRevision(workspace).ok, false); }; test("constructor dependency input contracts match the selected constructor", () => { const workspace = makeValidCapabilityWorkspace(); - const port = workspace.packageImports.flatMap((pkg) => pkg.exports).flatMap((entry) => entry.dependencyPorts) + const port = workspace.packageImports + .flatMap((pkg) => pkg.exports) + .flatMap((entry) => entry.dependencyPorts) .find((port) => port.requirement.kind === "constructor")!; assert.equal(port.requirement.kind, "constructor"); if (port.requirement.kind !== "constructor") return; @@ -52,14 +52,10 @@ const conformance = ( workspace: WorkspaceRevision, identity: Pick<(typeof workspace.conformances)[number], "atomId" | "interfaceRevisionId">, ) => { - const result = workspace.conformances.find((entry) => - entry.atomId === identity.atomId && - entry.interfaceRevisionId === identity.interfaceRevisionId - ); - assert.ok( - result, - `Missing fixture conformance ${identity.atomId} as ${identity.interfaceRevisionId}`, + const result = workspace.conformances.find( + (entry) => entry.atomId === identity.atomId && entry.interfaceRevisionId === identity.interfaceRevisionId, ); + assert.ok(result, `Missing fixture conformance ${identity.atomId} as ${identity.interfaceRevisionId}`); return result; }; @@ -70,12 +66,7 @@ test("the representative v1 workspace compiles to native and package plans", () assert.equal(compiled.ok, true); if (!compiled.ok) return; - const title = resolveOperationPlan( - compiled.plan, - fixtureId.project, - fixtureId.namedV1, - fixtureId.namedGet, - ); + const title = resolveOperationPlan(compiled.plan, fixtureId.project, fixtureId.namedV1, fixtureId.namedGet); assert.equal(title?.kind, "state"); if (title?.kind === "state") { assert.equal(title.binding.slotId, fixtureId.projectTitle); @@ -83,12 +74,7 @@ test("the representative v1 workspace compiles to native and package plans", () assert.deepEqual(title.attachment.owner, { kind: "workspace" }); } - const owner = resolveOperationPlan( - compiled.plan, - fixtureId.project, - fixtureId.ownedV1, - fixtureId.ownerResolve, - ); + const owner = resolveOperationPlan(compiled.plan, fixtureId.project, fixtureId.ownedV1, fixtureId.ownerResolve); assert.equal(owner?.kind, "edge"); if (owner?.kind === "edge") { assert.equal(owner.binding.edgeTypeId, fixtureId.projectOwner); @@ -98,12 +84,7 @@ test("the representative v1 workspace compiles to native and package plans", () }); } - const summary = resolveOperationPlan( - compiled.plan, - fixtureId.project, - fixtureId.summaryV1, - fixtureId.summaryGet, - ); + const summary = resolveOperationPlan(compiled.plan, fixtureId.project, fixtureId.summaryV1, fixtureId.summaryGet); assert.equal(summary?.kind, "package"); if (summary?.kind === "package") { assert.equal(summary.packageRevision.revisionId, fixtureId.todoRuntimeV1); @@ -126,10 +107,7 @@ test("the closure is an exact tree-shaking boundary", () => { const closure = computeCapabilityClosure(result.plan, [ { atomId: fixtureId.project, interfaceRevisionId: fixtureId.summaryV1 }, ]); - assert.deepEqual(closure.conformances, [ - fixtureId.projectNamedConformance, - fixtureId.projectSummaryConformance, - ]); + assert.deepEqual(closure.conformances, [fixtureId.projectNamedConformance, fixtureId.projectSummaryConformance]); assert.deepEqual(closure.packageRevisionIds, [fixtureId.todoRuntimeV1]); assert.deepEqual(closure.attachmentIds, [fixtureId.projectTitle]); assert.deepEqual(closure.constructorAtomIds, [fixtureId.person]); @@ -140,18 +118,12 @@ test("compiled plans are snapshots, not mutable authoring state", () => { const result = compileWorkspaceRevision(workspace); assert.equal(result.ok, true); if (!result.ok) return; - conformance(workspace, fixtureId.projectNamedConformance) - .operationBindings[0]!.binding = { - kind: "state", - slotId: fixtureId.personName, - primitive: "read", - }; - const resolved = resolveOperationPlan( - result.plan, - fixtureId.project, - fixtureId.namedV1, - fixtureId.namedGet, - ); + conformance(workspace, fixtureId.projectNamedConformance).operationBindings[0]!.binding = { + kind: "state", + slotId: fixtureId.personName, + primitive: "read", + }; + const resolved = resolveOperationPlan(result.plan, fixtureId.project, fixtureId.namedV1, fixtureId.namedGet); assert.equal(resolved?.kind, "state"); if (resolved?.kind === "state") { assert.equal(resolved.binding.slotId, fixtureId.projectTitle); @@ -164,20 +136,14 @@ test("a conformance binds every operation exactly once", () => { expectIssue(missing, "missing-operation-binding"); const duplicate = makeValidCapabilityWorkspace(); - const bindings = conformance( - duplicate, - fixtureId.projectNamedConformance, - ).operationBindings; + const bindings = conformance(duplicate, fixtureId.projectNamedConformance).operationBindings; bindings.push(structuredClone(bindings[0]!)); expectIssue(duplicate, "duplicate-operation-binding"); }); test("private attachments are visible only to their owning conformance", () => { const workspace = makeValidCapabilityWorkspace(); - const binding = conformance( - workspace, - fixtureId.projectNamedConformance, - ).operationBindings[0]!.binding; + const binding = conformance(workspace, fixtureId.projectNamedConformance).operationBindings[0]!.binding; assert.equal(binding.kind, "state"); if (binding.kind !== "state") return; binding.slotId = fixtureId.personName; @@ -186,15 +152,10 @@ test("private attachments are visible only to their owning conformance", () => { test("related-object dependency views cannot traverse another conformance's private edge", () => { const workspace = makeValidCapabilityWorkspace(); - const packageBinding = conformance( - workspace, - fixtureId.projectSummaryConformance, - ).operationBindings[0]!.binding; + const packageBinding = conformance(workspace, fixtureId.projectSummaryConformance).operationBindings[0]!.binding; assert.equal(packageBinding.kind, "package"); if (packageBinding.kind !== "package") return; - const named = packageBinding.dependencies.find( - (dependency) => dependency.portId === fixtureId.namedPort, - ); + const named = packageBinding.dependencies.find((dependency) => dependency.portId === fixtureId.namedPort); assert.ok(named && named.binding.kind === "interface"); named.binding.via = { edgeTypeId: fixtureId.projectOwner, @@ -205,20 +166,31 @@ test("related-object dependency views cannot traverse another conformance's priv const edge = owner.privateAttachments.find((entry) => entry.kind === "edge" && entry.id === fixtureId.projectOwner); assert.ok(edge?.kind === "edge"); edge.endpoints.find((endpoint) => endpoint.projectionId === fixtureId.projectOwnerProjection)!.publicTraversal = true; - assert.equal(validateWorkspaceRevision(workspace).some((entry) => entry.code === "private-attachment-access"), false, "Only an explicitly exported read-only traversal crosses ownership"); + assert.equal( + validateWorkspaceRevision(workspace).some((entry) => entry.code === "private-attachment-access"), + false, + "Only an explicitly exported read-only traversal crosses ownership", + ); }); test("public traversal permits a native inverse read without exporting mutation authority", () => { const workspace = makeValidCapabilityWorkspace(); const owned = conformance(workspace, fixtureId.projectOwnedConformance); - const index = owned.privateAttachments.findIndex(entry => entry.kind === "edge" && entry.id === fixtureId.projectOwner); + const index = owned.privateAttachments.findIndex( + (entry) => entry.kind === "edge" && entry.id === fixtureId.projectOwner, + ); const edge = owned.privateAttachments.splice(index, 1)[0]; assert.ok(edge?.kind === "edge"); conformance(workspace, fixtureId.projectNamedConformance).privateAttachments.push(edge); expectIssue(workspace, "private-attachment-access"); - edge.endpoints.find(endpoint => endpoint.projectionId === fixtureId.projectOwnerProjection)!.publicTraversal = true; + edge.endpoints.find((endpoint) => endpoint.projectionId === fixtureId.projectOwnerProjection)!.publicTraversal = true; const readPath = `conformances[${workspace.conformances.indexOf(owned)}].operationBindings[0]`; - assert.equal(validateWorkspaceRevision(workspace).some(issue => issue.code === "private-attachment-access" && issue.path.startsWith(readPath)), false); + assert.equal( + validateWorkspaceRevision(workspace).some( + (issue) => issue.code === "private-attachment-access" && issue.path.startsWith(readPath), + ), + false, + ); const binding = owned.operationBindings[0].binding; assert.ok(binding.kind === "edge"); binding.primitive = "connect"; @@ -227,19 +199,13 @@ test("public traversal permits a native inverse read without exporting mutation test("native state and edge providers must match operation shape", () => { const stateWorkspace = makeValidCapabilityWorkspace(); - const state = conformance( - stateWorkspace, - fixtureId.projectNamedConformance, - ).operationBindings[0]!.binding; + const state = conformance(stateWorkspace, fixtureId.projectNamedConformance).operationBindings[0]!.binding; assert.equal(state.kind, "state"); if (state.kind === "state") state.primitive = "write"; expectIssue(stateWorkspace, "invalid-state-binding"); const edgeWorkspace = makeValidCapabilityWorkspace(); - const edge = conformance( - edgeWorkspace, - fixtureId.projectOwnedConformance, - ).operationBindings[0]!.binding; + const edge = conformance(edgeWorkspace, fixtureId.projectOwnedConformance).operationBindings[0]!.binding; assert.equal(edge.kind, "edge"); if (edge.kind === "edge") edge.primitive = "connect"; expectIssue(edgeWorkspace, "invalid-edge-binding"); @@ -247,18 +213,13 @@ test("native state and edge providers must match operation shape", () => { test("package dependencies are complete, exact, and explicitly injected", () => { const missing = makeValidCapabilityWorkspace(); - const packageBinding = conformance( - missing, - fixtureId.projectSummaryConformance, - ).operationBindings[0]!.binding; + const packageBinding = conformance(missing, fixtureId.projectSummaryConformance).operationBindings[0]!.binding; assert.equal(packageBinding.kind, "package"); if (packageBinding.kind === "package") packageBinding.dependencies.pop(); expectIssue(missing, "invalid-dependency-binding"); const wrongType = makeValidCapabilityWorkspace(); - const summaryExport = wrongType.packageImports[0]!.exports.find( - (entry) => entry.id === fixtureId.summaryGetExport, - ); + const summaryExport = wrongType.packageImports[0]!.exports.find((entry) => entry.id === fixtureId.summaryGetExport); assert.ok(summaryExport); summaryExport.dependencyPorts[0]!.requirement = { kind: "state", @@ -270,19 +231,14 @@ test("package dependencies are complete, exact, and explicitly injected", () => test("package receiver requirements cannot depend on themselves", () => { const workspace = makeValidCapabilityWorkspace(); - const summaryExport = workspace.packageImports[0]!.exports.find( - (entry) => entry.id === fixtureId.summaryGetExport, - ); + const summaryExport = workspace.packageImports[0]!.exports.find((entry) => entry.id === fixtureId.summaryGetExport); assert.ok(summaryExport && summaryExport.kind === "operation"); summaryExport.receiverRequirement = { kind: "all-interfaces", interfaceRevisionIds: [fixtureId.summaryV1], }; summaryExport.dependencyPorts = []; - const packageBinding = conformance( - workspace, - fixtureId.projectSummaryConformance, - ).operationBindings[0]!.binding; + const packageBinding = conformance(workspace, fixtureId.projectSummaryConformance).operationBindings[0]!.binding; assert.equal(packageBinding.kind, "package"); if (packageBinding.kind === "package") packageBinding.dependencies = []; expectIssue(workspace, "cyclic-conformance-requirement"); diff --git a/test/evolution.test.ts b/test/evolution.test.ts index c0ccba8..93c91b1 100644 --- a/test/evolution.test.ts +++ b/test/evolution.test.ts @@ -1,13 +1,30 @@ import assert from "node:assert/strict"; import { test } from "node:test"; import { capabilityId as id, valueType, validateWorkspaceRevision } from "../src/capability-model/index.js"; -import { contentDigest, planEvolution, runtimeContracts, storageContracts, storageChangeRequiresMigration } from "../src/capability-model/evolution.js"; -import { capabilityFixtureSource, capabilityResourceSources, compileCapabilityFixture, makeValidCapabilityWorkspace } from "./fixtures/capability-model.js"; +import { + contentDigest, + planEvolution, + runtimeContracts, + storageContracts, + storageChangeRequiresMigration, +} from "../src/capability-model/evolution.js"; +import { + capabilityFixtureSource, + capabilityResourceSources, + compileCapabilityFixture, + makeValidCapabilityWorkspace, +} from "./fixtures/capability-model.js"; test("QX carries stable conformance IDs and implementation semantic majors", () => { const result = compileCapabilityFixture({ - workspace: capabilityFixtureSource.replace("conform Project as Named {", 'conform Project as Named id "conformance:project:named" semantic-major 3 {'), - todo: capabilityResourceSources.todo.replace('revision "package:todo-runtime@1" {', 'revision "package:todo-runtime@1" semantic-major 2 {'), + workspace: capabilityFixtureSource.replace( + "conform Project as Named {", + 'conform Project as Named id "conformance:project:named" semantic-major 3 {', + ), + todo: capabilityResourceSources.todo.replace( + 'revision "package:todo-runtime@1" {', + 'revision "package:todo-runtime@1" semantic-major 2 {', + ), }); assert.ok(result.ok, JSON.stringify(result)); assert.equal(result.workspace.conformances[0]!.id, "conformance:project:named"); @@ -43,16 +60,32 @@ test("a workspace root change and display names do not restart package runtimes" after.conformances.reverse(); after.interfaceImports.reverse(); const report = planEvolution(before, after, { allowLegacy: true }); - assert.deepEqual(report.runtimeActions.map((entry) => entry.action), ["keep"]); + assert.deepEqual( + report.runtimeActions.map((entry) => entry.action), + ["keep"], + ); assert.deepEqual(report.storageChanges, []); assert.deepEqual(report.packageChecks, []); }); test("consumer changes preserve unrelated resource owners", () => { const before = makeValidCapabilityWorkspace(); - before.packageImports.push({ packageId: id.package("package:resource-owner"), revisionId: id.packageRevision("package:resource-owner@1"), - displayName: "ResourceOwner", source: { repository: "https://example.org/owner.git", commit: "a".repeat(40) }, - exports: [{ kind: "function", id: id.packageExport("export:owner:ping"), displayName: "ping", inputType: valueType.unit, outputType: valueType.unit, dependencyPorts: [] }] }); + before.packageImports.push({ + packageId: id.package("package:resource-owner"), + revisionId: id.packageRevision("package:resource-owner@1"), + displayName: "ResourceOwner", + source: { repository: "https://example.org/owner.git", commit: "a".repeat(40) }, + exports: [ + { + kind: "function", + id: id.packageExport("export:owner:ping"), + displayName: "ping", + inputType: valueType.unit, + outputType: valueType.unit, + dependencyPorts: [], + }, + ], + }); const after = structuredClone(before); after.packageImports[0]!.source.commit = "e".repeat(40); const actions = planEvolution(before, after, { allowLegacy: true }).runtimeActions; @@ -82,8 +115,12 @@ test("semantic review receipts cover exact consumer/provider contracts", () => { const report = planEvolution(before, after, { allowLegacy: true }); assert.equal(report.reviews.length, 1); assert.equal(report.reviews[0]!.accepted, false); - const receipt = { requirementDigest: report.reviews[0]!.requirementDigest, decision: "accepted-unchanged" as const, - rationale: "Reviewed the semantic change against summary behavior", agentId: "workspace-agent" }; + const receipt = { + requirementDigest: report.reviews[0]!.requirementDigest, + decision: "accepted-unchanged" as const, + rationale: "Reviewed the semantic change against summary behavior", + agentId: "workspace-agent", + }; assert.equal(planEvolution(before, after, { allowLegacy: true, reviews: [receipt] }).reviews[0]!.accepted, true); after.packageImports[0]!.source.commit = "f".repeat(40); assert.equal(planEvolution(before, after, { allowLegacy: true, reviews: [receipt] }).reviews[0]!.accepted, false); @@ -95,15 +132,18 @@ test("evolution enrollment is explicit and never erases legacy ownership", () => assert.ok(report.blockers.some((entry) => entry.includes("workspace-shared"))); assert.ok(report.blockers.some((entry) => entry.includes("authored ID"))); assert.equal(runtimeContracts(workspace).length, 1); - assert.throws(() => planEvolution(workspace, { ...workspace, workspaceId: id.workspace("other") }), /different workspace/); + assert.throws( + () => planEvolution(workspace, { ...workspace, workspaceId: id.workspace("other") }), + /different workspace/, + ); }); test("automatic preservation distinguishes additions and defaults from incompatible storage", () => { const workspace = makeValidCapabilityWorkspace(); - const before = storageContracts(workspace).find(entry => entry.kind === "state")!; + const before = storageContracts(workspace).find((entry) => entry.kind === "state")!; const next = structuredClone(before); const value = next.definition as Record; - value.defaultValue = {displayName: "important user data"}; + value.defaultValue = { displayName: "important user data" }; assert.equal(storageChangeRequiresMigration(before, next, new Set()), false); next.ownerId = "another-owner"; assert.equal(storageChangeRequiresMigration(before, next, new Set()), true); @@ -111,10 +151,10 @@ test("automatic preservation distinguishes additions and defaults from incompati delete value.defaultValue; assert.equal(storageChangeRequiresMigration(undefined, next, new Set([value.attachedTo as string])), true); assert.equal(storageChangeRequiresMigration(undefined, next, new Set()), false); - const slot = workspace.sharedAttachments.find(entry => entry.kind === "state")!; + const slot = workspace.sharedAttachments.find((entry) => entry.kind === "state")!; if (slot.kind !== "state") throw new Error("fixture slot"); - slot.defaultValue = {displayName: "one"}; + slot.defaultValue = { displayName: "one" }; const first = storageContracts(workspace); - slot.defaultValue = {displayName: "two"}; + slot.defaultValue = { displayName: "two" }; assert.notDeepEqual(storageContracts(workspace), first, "user data must not be stripped as schema metadata"); }); diff --git a/test/file-lock.test.ts b/test/file-lock.test.ts index 2a09875..72cc543 100644 --- a/test/file-lock.test.ts +++ b/test/file-lock.test.ts @@ -1,34 +1,44 @@ import test from "node:test"; import assert from "node:assert/strict"; -import {mkdtemp, rm} from "node:fs/promises"; +import { mkdtemp, rm } from "node:fs/promises"; import path from "node:path"; import os from "node:os"; -import {spawn} from "node:child_process"; -import {once} from "node:events"; -import {withFileLock} from "../src/capability-language/file-lock.js"; +import { spawn } from "node:child_process"; +import { once } from "node:events"; +import { withFileLock } from "../src/capability-language/file-lock.js"; -test("authoring lock excludes concurrent mutations and survives owner death", async context => { +test("authoring lock excludes concurrent mutations and survives owner death", async (context) => { const root = await mkdtemp(path.join(os.tmpdir(), "qx-lock-test-")); - context.after(() => rm(root, {recursive: true, force: true})); + context.after(() => rm(root, { recursive: true, force: true })); const filename = path.join(root, "lock"); const events: string[] = []; let queued: Promise; await withFileLock(filename, async () => { - queued = withFileLock(filename, async () => {events.push("second");}); + queued = withFileLock(filename, async () => { + events.push("second"); + }); events.push("first"); }); await queued!; assert.deepEqual(events, ["first", "second"]); const module = new URL("../src/capability-language/file-lock.js", import.meta.url).href; - const owner = spawn(process.execPath, ["--input-type=module", "-e", - `import {withFileLock} from ${JSON.stringify(module)}; await withFileLock(${JSON.stringify(filename)}, async () => {process.stdout.write('ready'); await new Promise(() => {});});`], - {stdio: ["ignore", "pipe", "pipe"]}); + const owner = spawn( + process.execPath, + [ + "--input-type=module", + "-e", + `import {withFileLock} from ${JSON.stringify(module)}; await withFileLock(${JSON.stringify(filename)}, async () => {process.stdout.write('ready'); await new Promise(() => {});});`, + ], + { stdio: ["ignore", "pipe", "pipe"] }, + ); context.after(() => owner.kill("SIGKILL")); await once(owner.stdout, "data"); const exited = once(owner, "exit"); owner.kill("SIGKILL"); await exited; let acquired = false; - await withFileLock(filename, async () => {acquired = true;}); + await withFileLock(filename, async () => { + acquired = true; + }); assert.equal(acquired, true); }); diff --git a/test/fixtures/capability-model.ts b/test/fixtures/capability-model.ts index ca51727..ace58c5 100644 --- a/test/fixtures/capability-model.ts +++ b/test/fixtures/capability-model.ts @@ -1,18 +1,12 @@ import { readFileSync } from "node:fs"; import { resolve } from "node:path"; -import { - capabilityId, - type WorkspaceRevision, -} from "../../src/capability-model/index.js"; +import { capabilityId, type WorkspaceRevision } from "../../src/capability-model/index.js"; import { compileCapabilityResourceSource, compileCapabilitySource, type CapabilityImportEnvironment, } from "../../src/capability-language/index.js"; -import type { - InterfaceRevision, - PackageRevision, -} from "../../src/capability-model/index.js"; +import type { InterfaceRevision, PackageRevision } from "../../src/capability-model/index.js"; export const fixtureId = { workspace: capabilityId.workspace("workspace:todo"), @@ -29,9 +23,7 @@ export const fixtureId = { projectTitle: capabilityId.slot("slot:project:title"), personName: capabilityId.slot("slot:person:name"), projectOwner: capabilityId.edgeType("edge:project:owner"), - projectOwnerProjection: capabilityId.edgeProjection( - "projection:project-owner:owner", - ), + projectOwnerProjection: capabilityId.edgeProjection("projection:project-owner:owner"), projectNamedConformance: { atomId: capabilityId.atom("atom:project"), interfaceRevisionId: capabilityId.interfaceRevision("interface:named@1"), @@ -49,31 +41,18 @@ export const fixtureId = { interfaceRevisionId: capabilityId.interfaceRevision("interface:summary@1"), }, todoRuntimeV1: capabilityId.packageRevision("package:todo-runtime@1"), - summaryGetExport: capabilityId.packageExport( - "export:todo-runtime:summary-get", - ), - createPersonExport: capabilityId.packageExport( - "export:todo-runtime:create-person", - ), + summaryGetExport: capabilityId.packageExport("export:todo-runtime:summary-get"), + createPersonExport: capabilityId.packageExport("export:todo-runtime:create-person"), titlePort: capabilityId.dependencyPort("port:summary:title"), namedPort: capabilityId.dependencyPort("port:summary:named"), personPort: capabilityId.dependencyPort("port:summary:person"), } as const; -export const capabilityFixturePath = resolve( - process.cwd(), - "test/fixtures/todo.capabilities.qx", -); +export const capabilityFixturePath = resolve(process.cwd(), "test/fixtures/todo.capabilities.qx"); -export const capabilityFixtureSource = readFileSync( - capabilityFixturePath, - "utf8", -); +export const capabilityFixtureSource = readFileSync(capabilityFixturePath, "utf8"); -const resourceSource = (name: string) => readFileSync( - resolve(process.cwd(), "test/fixtures", name), - "utf8", -); +const resourceSource = (name: string) => readFileSync(resolve(process.cwd(), "test/fixtures", name), "utf8"); export const capabilityResourceSources = { named: resourceSource("named.interface.qx"), @@ -85,110 +64,74 @@ export const capabilityResourceSources = { const source = (repository: string, commit: string) => ({ repository, commit }); const interfaceRevision = ( - resource: { kind: "interface"; revision: InterfaceRevision } | - { kind: "package"; revision: PackageRevision }, + resource: { kind: "interface"; revision: InterfaceRevision } | { kind: "package"; revision: PackageRevision }, ) => { if (resource.kind !== "interface") throw new Error("Expected interface fixture"); return resource.revision; }; const packageRevision = ( - resource: { kind: "interface"; revision: InterfaceRevision } | - { kind: "package"; revision: PackageRevision }, + resource: { kind: "interface"; revision: InterfaceRevision } | { kind: "package"; revision: PackageRevision }, ) => { if (resource.kind !== "package") throw new Error("Expected package fixture"); return resource.revision; }; -export const compileCapabilityFixture = (overrides: Partial<{ - workspace: string; - named: string; - owned: string; - summary: string; - todo: string; -}> = {}) => { - const named = compileCapabilityResourceSource( - overrides.named ?? capabilityResourceSources.named, - { - source: source( - "https://repos.quixos.org/quixos-todo/interface-named.git", - "2".repeat(40), - ), - fileName: "named.interface.qx", - }, - ); +export const compileCapabilityFixture = ( + overrides: Partial<{ + workspace: string; + named: string; + owned: string; + summary: string; + todo: string; + }> = {}, +) => { + const named = compileCapabilityResourceSource(overrides.named ?? capabilityResourceSources.named, { + source: source("https://repos.quixos.org/quixos-todo/interface-named.git", "2".repeat(40)), + fileName: "named.interface.qx", + }); if (!named.ok) return named; const namedRevision = interfaceRevision(named.resource); const namedEnvironment: CapabilityImportEnvironment = { interfaces: new Map([["Named", namedRevision]]), interfaceClosure: [namedRevision], }; - const owned = compileCapabilityResourceSource( - overrides.owned ?? capabilityResourceSources.owned, - { - source: source( - "https://repos.quixos.org/quixos-todo/interface-owned.git", - "3".repeat(40), - ), - fileName: "owned.interface.qx", - environment: namedEnvironment, - }, - ); + const owned = compileCapabilityResourceSource(overrides.owned ?? capabilityResourceSources.owned, { + source: source("https://repos.quixos.org/quixos-todo/interface-owned.git", "3".repeat(40)), + fileName: "owned.interface.qx", + environment: namedEnvironment, + }); if (!owned.ok) return owned; const ownedRevision = interfaceRevision(owned.resource); - const summary = compileCapabilityResourceSource( - overrides.summary ?? capabilityResourceSources.summary, - { - source: source( - "https://repos.quixos.org/quixos-todo/interface-summary.git", - "4".repeat(40), - ), - fileName: "summary.interface.qx", - }, - ); + const summary = compileCapabilityResourceSource(overrides.summary ?? capabilityResourceSources.summary, { + source: source("https://repos.quixos.org/quixos-todo/interface-summary.git", "4".repeat(40)), + fileName: "summary.interface.qx", + }); if (!summary.ok) return summary; const summaryRevision = interfaceRevision(summary.resource); - const todo = compileCapabilityResourceSource( - overrides.todo ?? capabilityResourceSources.todo, - { - source: source( - "https://repos.quixos.org/quixos-todo/package-todo-runtime.git", - "5".repeat(40), - ), - fileName: "todo.package.qx", - environment: namedEnvironment, - }, - ); + const todo = compileCapabilityResourceSource(overrides.todo ?? capabilityResourceSources.todo, { + source: source("https://repos.quixos.org/quixos-todo/package-todo-runtime.git", "5".repeat(40)), + fileName: "todo.package.qx", + environment: namedEnvironment, + }); if (!todo.ok) return todo; const todoRevision = packageRevision(todo.resource); - return compileCapabilitySource( - overrides.workspace ?? capabilityFixtureSource, - capabilityFixturePath, - { - interfaces: new Map([ - ["Named", namedRevision], - ["Owned", ownedRevision], - ["Summary", summaryRevision], - ]), - packages: new Map([["TodoRuntime", todoRevision]]), - interfaceClosure: [ - namedRevision, - ownedRevision, - summaryRevision, - ], - packageClosure: [todoRevision], - }, - ); + return compileCapabilitySource(overrides.workspace ?? capabilityFixtureSource, capabilityFixturePath, { + interfaces: new Map([ + ["Named", namedRevision], + ["Owned", ownedRevision], + ["Summary", summaryRevision], + ]), + packages: new Map([["TodoRuntime", todoRevision]]), + interfaceClosure: [namedRevision, ownedRevision, summaryRevision], + packageClosure: [todoRevision], + }); }; export const makeValidCapabilityWorkspace = (): WorkspaceRevision => { const result = compileCapabilityFixture(); if (!result.ok) { - throw new Error( - result.diagnostics - .map((entry) => `${entry.phase}/${entry.code}: ${entry.message}`) - .join("\n"), - ); + throw new Error(result.diagnostics.map((entry) => `${entry.phase}/${entry.code}: ${entry.message}`).join("\n")); } return structuredClone(result.workspace); }; diff --git a/test/fixtures/todo.capabilities.qx b/test/fixtures/todo.capabilities.qx index 2c10fd1..466277e 100644 --- a/test/fixtures/todo.capabilities.qx +++ b/test/fixtures/todo.capabilities.qx @@ -8,7 +8,7 @@ workspace Todo id "workspace:todo" revision "workspace:todo@1" commit "111111111 import package TodoRuntime; shared state ProjectTitle id "slot:project:title" on Project : string - policy optimistic-register default "Untitled project"; + policy optimistic-register default "Untitled project"; conform Project as Named { bind name.get to state ProjectTitle.read; @@ -19,7 +19,7 @@ workspace Todo id "workspace:todo" revision "workspace:todo@1" commit "111111111 conform Person as Named { private state PersonName id "slot:person:name" on Person : string - policy optimistic-register default "Anonymous"; + policy optimistic-register default "Anonymous"; bind name.get to state PersonName.read; bind name.set to state PersonName.write; bind name.watch-start to state PersonName.watch-start; diff --git a/test/fixtures/todo.package.qx b/test/fixtures/todo.package.qx index c1c2143..f070186 100644 --- a/test/fixtures/todo.package.qx +++ b/test/fixtures/todo.package.qx @@ -3,10 +3,10 @@ external atom Person id "atom:person"; package TodoRuntime id "package:todo-runtime" revision "package:todo-runtime@1" { operation summaryGet id "export:todo-runtime:summary-get" : unit -> string - mode call receiver interfaces [Named] requires { - state title id "port:summary:title" : string [read]; - interface named id "port:summary:named" : Named; - constructor person id "port:summary:person" : Person; - }; + mode call receiver interfaces [Named] requires { + state title id "port:summary:title" : string [read]; + interface named id "port:summary:named" : Named; + constructor person id "port:summary:person" : Person; + }; constructor createPerson id "export:todo-runtime:create-person" constructs Person : unit; } diff --git a/test/fixtures/web-studio.capabilities.qx b/test/fixtures/web-studio.capabilities.qx index 6467910..5ead8b3 100644 --- a/test/fixtures/web-studio.capabilities.qx +++ b/test/fixtures/web-studio.capabilities.qx @@ -8,7 +8,7 @@ workspace WebStudioFixture id "workspace:web-studio-fixture" revision "workspace import package ComponentRuntime; shared state ProjectName id "slot:project:name" on Project : string - policy optimistic-register default "Untitled project"; + policy optimistic-register default "Untitled project"; shared edge ProjectComponentEdge id "edge:project:component" { atom ProjectComponent projection subject id "projection:component:subject" exactly-one; atom Project projection component id "projection:project:component" optional-one; diff --git a/test/git-resolver.test.ts b/test/git-resolver.test.ts index 6e6a96e..5df84f2 100644 --- a/test/git-resolver.test.ts +++ b/test/git-resolver.test.ts @@ -8,7 +8,7 @@ import { promisify } from "node:util"; import { createGitCapabilityResolver } from "../src/capability-language/git-resolver.js"; const execFile = promisify(callback); -test("dependency resolution is reusable across processes, concurrent and rejects modified checkouts", async context => { +test("dependency resolution is reusable across processes, concurrent and rejects modified checkouts", async (context) => { const temporary = await mkdtemp(path.join(os.tmpdir(), "qx-resolver-test-")); context.after(() => rm(temporary, { recursive: true, force: true })); const origin = path.join(temporary, "origin"); diff --git a/test/migrations.test.ts b/test/migrations.test.ts index 1ddf1df..51f22b1 100644 --- a/test/migrations.test.ts +++ b/test/migrations.test.ts @@ -1,28 +1,67 @@ import test from "node:test"; import assert from "node:assert/strict"; -import { contentDigest, validateMigrationCatalog, selectMigrationPath, type MigrationCatalog } from "../src/capability-model/index.js"; -const before = { fields: ["name"] }, after = { fields: ["first", "last"] }; -const from = contentDigest(before), to = contentDigest(after); -const catalog = (): MigrationCatalog => ({ schemaVersion: 1, contracts: { [from]: before, [to]: after }, migrations: [{ - id: "split-name", scopeId: "person-name", from, to, - implementation: { exportId: "migrate-name", file: "src/migrate-name.ts", digest: contentDigest("implementation") }, predecessors: [], - ports: [{ name: "source", view: "old", access: ["read"], contractDigest: from }, { name: "target", view: "new", access: ["write"], contractDigest: to }], -}] }); +import { + contentDigest, + validateMigrationCatalog, + selectMigrationPath, + type MigrationCatalog, +} from "../src/capability-model/index.js"; +const before = { fields: ["name"] }, + after = { fields: ["first", "last"] }; +const from = contentDigest(before), + to = contentDigest(after); +const catalog = (): MigrationCatalog => ({ + schemaVersion: 1, + contracts: { [from]: before, [to]: after }, + migrations: [ + { + id: "split-name", + scopeId: "person-name", + from, + to, + implementation: { + exportId: "migrate-name", + file: "src/migrate-name.ts", + digest: contentDigest("implementation"), + }, + predecessors: [], + ports: [ + { name: "source", view: "old", access: ["read"], contractDigest: from }, + { name: "target", view: "new", access: ["write"], contractDigest: to }, + ], + }, + ], +}); test("published migrations retain exact old contracts and explicit local bindings", () => { const value = validateMigrationCatalog(catalog(), new Set(["migrate-name"])); - const selection = { scopeId: "person-name", from, to, path: ["split-name"], bindings: { source: "old-slot", target: "new-slot" } }; + const selection = { + scopeId: "person-name", + from, + to, + path: ["split-name"], + bindings: { source: "old-slot", target: "new-slot" }, + }; const result = selectMigrationPath(value, selection); assert.equal(result[0].alreadyApplied, false); - assert.equal(selectMigrationPath(value, selection, new Map([["split-name", result[0].digest]]))[0].alreadyApplied, true); - const changed = catalog(); changed.migrations[0].implementation.digest = contentDigest("new code"); - assert.throws(() => selectMigrationPath(changed, selection, new Map([["split-name", result[0].digest]])), /different code/); + assert.equal( + selectMigrationPath(value, selection, new Map([["split-name", result[0].digest]]))[0].alreadyApplied, + true, + ); + const changed = catalog(); + changed.migrations[0].implementation.digest = contentDigest("new code"); + assert.throws( + () => selectMigrationPath(changed, selection, new Map([["split-name", result[0].digest]])), + /different code/, + ); assert.throws(() => selectMigrationPath(value, { ...selection, path: [] }), /does not cover/); assert.throws(() => selectMigrationPath(value, { ...selection, bindings: {} }), /Missing local/); }); test("old migration views cannot be writable and contract hashes are verified", () => { - const value = catalog(); value.migrations[0].ports[0].access = ["write"]; + const value = catalog(); + value.migrations[0].ports[0].access = ["write"]; assert.throws(() => validateMigrationCatalog(value), /read-only/); - const corrupt = catalog(); corrupt.contracts[from] = {}; + const corrupt = catalog(); + corrupt.contracts[from] = {}; assert.throws(() => validateMigrationCatalog(corrupt), /digest mismatch/); }); @@ -31,6 +70,6 @@ test("migration history rejects cycles and non-boolean compatibility promises", cyclic.migrations[0].predecessors = ["split-name"]; assert.throws(() => validateMigrationCatalog(cyclic), /Cyclic/); const misleading = catalog(); - (misleading.migrations[0] as unknown as {preservesOldReaders: string}).preservesOldReaders = "false"; + (misleading.migrations[0] as unknown as { preservesOldReaders: string }).preservesOldReaders = "false"; assert.throws(() => validateMigrationCatalog(misleading), /must be booleans/); }); diff --git a/test/nix-candidate.test.ts b/test/nix-candidate.test.ts index 035b5d0..24823b7 100644 --- a/test/nix-candidate.test.ts +++ b/test/nix-candidate.test.ts @@ -7,28 +7,69 @@ import { execFile as callback } from "node:child_process"; import { promisify } from "node:util"; const execFile = promisify(callback); -test("Nix checks a retained immutable source without a local overlay and reuses the result", {skip: !process.env.QX_CHECK_GENERATOR}, async context => { - const root = await fs.mkdtemp(path.join(os.tmpdir(), "qx-nix-candidate-test-")); - context.after(() => fs.rm(root, {recursive: true, force: true})); - const git = (...args: string[]) => execFile("git", ["-C", root, ...args]); - await git("init"); - await fs.writeFile(path.join(root, "interface.qx"), 'interface Example id "interface:example" revision "interface:example@1" {}'); - await fs.writeFile(path.join(root, "quixos.lock"), `quixos-lock version 1 { quixos source { repository "https://example.test/quixos"; commit "${"a".repeat(40)}"; } }`); - await git("add", "."); - await git("-c", "user.name=Test", "-c", "user.email=test@example.test", "commit", "-m", "contract"); - const commit = (await git("rev-parse", "HEAD")).stdout.trim(); - await git("tag", `quixos-reachability/${commit}`); - const generator = process.env.QX_CHECK_GENERATOR!; - const repository = "https://immutable-candidate.example.test/contract.git"; - const env = {...process.env, GIT_CONFIG_COUNT: "1", GIT_CONFIG_KEY_0: `url.file://${root}.insteadOf`, GIT_CONFIG_VALUE_0: repository}; - const build = async () => (await execFile("nix", ["build", "--impure", "--file", path.join(generator, "share/checked-candidate.nix"), - "--argstr", "repository", repository, "--argstr", "commit", commit, - "--argstr", "kind", "interface", "--argstr", "generator", generator, - "--option", "substitute", "false", "--no-link", "--print-out-paths"], {env, maxBuffer: 4 * 1024 * 1024})).stdout.trim(); - const output = await build(); - const candidate = JSON.parse(await fs.readFile(path.join(output, "candidate.json"), "utf8")); - assert.equal(candidate.revision.source.commit, commit); - await fs.writeFile(path.join(root, "interface.qx"), "broken draft"); - assert.equal(await build(), output); - assert.deepEqual(JSON.parse(await fs.readFile(path.join(output, "checks.json"), "utf8")), []); -}); +test( + "Nix checks a retained immutable source without a local overlay and reuses the result", + { skip: !process.env.QX_CHECK_GENERATOR }, + async (context) => { + const root = await fs.mkdtemp(path.join(os.tmpdir(), "qx-nix-candidate-test-")); + context.after(() => fs.rm(root, { recursive: true, force: true })); + const git = (...args: string[]) => execFile("git", ["-C", root, ...args]); + await git("init"); + await fs.writeFile( + path.join(root, "interface.qx"), + 'interface Example id "interface:example" revision "interface:example@1" {}', + ); + await fs.writeFile( + path.join(root, "quixos.lock"), + `quixos-lock version 1 { quixos source { repository "https://example.test/quixos"; commit "${"a".repeat(40)}"; } }`, + ); + await git("add", "."); + await git("-c", "user.name=Test", "-c", "user.email=test@example.test", "commit", "-m", "contract"); + const commit = (await git("rev-parse", "HEAD")).stdout.trim(); + await git("tag", `quixos-reachability/${commit}`); + const generator = process.env.QX_CHECK_GENERATOR!; + const repository = "https://immutable-candidate.example.test/contract.git"; + const env = { + ...process.env, + GIT_CONFIG_COUNT: "1", + GIT_CONFIG_KEY_0: `url.file://${root}.insteadOf`, + GIT_CONFIG_VALUE_0: repository, + }; + const build = async () => + ( + await execFile( + "nix", + [ + "build", + "--impure", + "--file", + path.join(generator, "share/checked-candidate.nix"), + "--argstr", + "repository", + repository, + "--argstr", + "commit", + commit, + "--argstr", + "kind", + "interface", + "--argstr", + "generator", + generator, + "--option", + "substitute", + "false", + "--no-link", + "--print-out-paths", + ], + { env, maxBuffer: 4 * 1024 * 1024 }, + ) + ).stdout.trim(); + const output = await build(); + const candidate = JSON.parse(await fs.readFile(path.join(output, "candidate.json"), "utf8")); + assert.equal(candidate.revision.source.commit, commit); + await fs.writeFile(path.join(root, "interface.qx"), "broken draft"); + assert.equal(await build(), output); + assert.deepEqual(JSON.parse(await fs.readFile(path.join(output, "checks.json"), "utf8")), []); + }, +); diff --git a/test/pin-upgrades.test.ts b/test/pin-upgrades.test.ts index d715bd8..f6e30ef 100644 --- a/test/pin-upgrades.test.ts +++ b/test/pin-upgrades.test.ts @@ -3,100 +3,179 @@ import assert from "node:assert/strict"; import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import {promisify} from "node:util"; -import {execFile as callback} from "node:child_process"; -import {planPinUpgrades, applyPinUpgrades, type UpgradeEffects} from "../src/capability-language/pin-upgrades.js"; +import { promisify } from "node:util"; +import { execFile as callback } from "node:child_process"; +import { planPinUpgrades, applyPinUpgrades, type UpgradeEffects } from "../src/capability-language/pin-upgrades.js"; const execFile = promisify(callback); -test("real jj snapshots and immutable Git publication propagate a changed interface into the root", {skip: !process.env.QX_CHECK_GENERATOR}, async (context) => { - const workbench = await fs.mkdtemp(path.join(os.tmpdir(), "qx-real-upgrade-")); - context.after(() => fs.rm(workbench, {recursive: true, force: true})); - // Exercise the actual effects with local bare remotes, without external writes. - const environment = { - GIT_CONFIG_COUNT: "1", GIT_CONFIG_KEY_0: `url.file://${workbench}/remotes/.insteadOf`, - GIT_CONFIG_VALUE_0: "https://upgrade.test/", QUIXOS_JJ_NO_CHECKPOINT: "1", - QUIXOS_CHECK_GENERATOR: process.env.QX_CHECK_GENERATOR!, - }; - const previous = Object.fromEntries(Object.keys(environment).map(key => [key, process.env[key]])); - Object.assign(process.env, environment); - context.after(() => {for (const [key, value] of Object.entries(previous)) if (value === undefined) delete process.env[key]; else process.env[key] = value;}); - const run = async (cwd: string, command: string, args: string[]) => (await execFile(command, args, {cwd})).stdout.trim(); - await fs.mkdir(path.join(workbench, "remotes")); - const nodes = []; - for (const [kind, directory, remote] of [["interface", "resources/Named", "named.git"], ["workspace", "root", "workspace.git"]] as const) { - const root = path.join(workbench, directory); - await fs.mkdir(root, {recursive: true}); - await run(workbench, "git", ["init", "--bare", path.join(workbench, "remotes", remote)]); - await run(root, "jj", ["git", "init", "--colocate"]); - await run(root, "git", ["remote", "add", "origin", `https://upgrade.test/${remote}`]); - await fs.writeFile(path.join(root, ".gitignore"), ".quixos/\n"); - const child = nodes[0]; - await fs.writeFile(path.join(root, "quixos.lock"), `quixos-lock version 1 { quixos source { repository "https://upgrade.test/quixos.git"; commit "${"a".repeat(40)}"; } ${child ? `interface Named source { repository "${child.source.repository}"; commit "${child.source.commit}"; }` : ""} }`); - await fs.writeFile(path.join(root, `${kind}.qx`), kind === "interface" ? 'interface Named id "interface:named" revision "interface:named@1" {}' : `workspace W id "workspace:w" revision "workspace:w@1" commit "${"a".repeat(40)}" { import interface Named; atom A id "atom:a"; }`); - await run(root, "jj", ["describe", "-m", "Initial source"]); - const commit = await run(root, "jj", ["log", "--no-graph", "-r", "@", "-T", "commit_id"]); - await run(root, "git", ["push", "origin", `${commit}:refs/tags/quixos-reachability/${commit}`]); - nodes.push({kind, directory, source: {repository: `https://upgrade.test/${remote}`, commit}}); - } - await fs.mkdir(path.join(workbench, ".quixos")); - await fs.writeFile(path.join(workbench, ".quixos/resource-graph.json"), JSON.stringify({resources: [nodes[0]]})); - await fs.appendFile(path.join(workbench, nodes[0].directory, "interface.qx"), "\n// incremental author edit\n"); - const plan = await planPinUpgrades(workbench, {nodes, bootstrap: true}); - const result = await applyPinUpgrades(plan); - assert.equal(result.activated, false); - const graph = JSON.parse(await fs.readFile(path.join(workbench, ".quixos/resource-graph.json"), "utf8")); - const commit = graph.resources[0].source.commit; - assert.notEqual(commit, nodes[0].source.commit); - assert.match(await fs.readFile(path.join(workbench, "root/quixos.lock"), "utf8"), new RegExp(commit)); - assert.match(await run(workbench, "git", ["--git-dir", path.join(workbench, "remotes/named.git"), "show-ref"]), new RegExp(commit)); -}); +test( + "real jj snapshots and immutable Git publication propagate a changed interface into the root", + { skip: !process.env.QX_CHECK_GENERATOR }, + async (context) => { + const workbench = await fs.mkdtemp(path.join(os.tmpdir(), "qx-real-upgrade-")); + context.after(() => fs.rm(workbench, { recursive: true, force: true })); + // Exercise the actual effects with local bare remotes, without external writes. + const environment = { + GIT_CONFIG_COUNT: "1", + GIT_CONFIG_KEY_0: `url.file://${workbench}/remotes/.insteadOf`, + GIT_CONFIG_VALUE_0: "https://upgrade.test/", + QUIXOS_JJ_NO_CHECKPOINT: "1", + QUIXOS_CHECK_GENERATOR: process.env.QX_CHECK_GENERATOR!, + }; + const previous = Object.fromEntries(Object.keys(environment).map((key) => [key, process.env[key]])); + Object.assign(process.env, environment); + context.after(() => { + for (const [key, value] of Object.entries(previous)) + if (value === undefined) delete process.env[key]; + else process.env[key] = value; + }); + const run = async (cwd: string, command: string, args: string[]) => + (await execFile(command, args, { cwd })).stdout.trim(); + await fs.mkdir(path.join(workbench, "remotes")); + const nodes = []; + for (const [kind, directory, remote] of [ + ["interface", "resources/Named", "named.git"], + ["workspace", "root", "workspace.git"], + ] as const) { + const root = path.join(workbench, directory); + await fs.mkdir(root, { recursive: true }); + await run(workbench, "git", ["init", "--bare", path.join(workbench, "remotes", remote)]); + await run(root, "jj", ["git", "init", "--colocate"]); + await run(root, "git", ["remote", "add", "origin", `https://upgrade.test/${remote}`]); + await fs.writeFile(path.join(root, ".gitignore"), ".quixos/\n"); + const child = nodes[0]; + await fs.writeFile( + path.join(root, "quixos.lock"), + `quixos-lock version 1 { quixos source { repository "https://upgrade.test/quixos.git"; commit "${"a".repeat(40)}"; } ${child ? `interface Named source { repository "${child.source.repository}"; commit "${child.source.commit}"; }` : ""} }`, + ); + await fs.writeFile( + path.join(root, `${kind}.qx`), + kind === "interface" + ? 'interface Named id "interface:named" revision "interface:named@1" {}' + : `workspace W id "workspace:w" revision "workspace:w@1" commit "${"a".repeat(40)}" { import interface Named; atom A id "atom:a"; }`, + ); + await run(root, "jj", ["describe", "-m", "Initial source"]); + const commit = await run(root, "jj", ["log", "--no-graph", "-r", "@", "-T", "commit_id"]); + await run(root, "git", ["push", "origin", `${commit}:refs/tags/quixos-reachability/${commit}`]); + nodes.push({ kind, directory, source: { repository: `https://upgrade.test/${remote}`, commit } }); + } + await fs.mkdir(path.join(workbench, ".quixos")); + await fs.writeFile(path.join(workbench, ".quixos/resource-graph.json"), JSON.stringify({ resources: [nodes[0]] })); + await fs.appendFile(path.join(workbench, nodes[0].directory, "interface.qx"), "\n// incremental author edit\n"); + const plan = await planPinUpgrades(workbench, { nodes, bootstrap: true }); + const result = await applyPinUpgrades(plan); + assert.equal(result.activated, false); + const graph = JSON.parse(await fs.readFile(path.join(workbench, ".quixos/resource-graph.json"), "utf8")); + const commit = graph.resources[0].source.commit; + assert.notEqual(commit, nodes[0].source.commit); + assert.match(await fs.readFile(path.join(workbench, "root/quixos.lock"), "utf8"), new RegExp(commit)); + assert.match( + await run(workbench, "git", ["--git-dir", path.join(workbench, "remotes/named.git"), "show-ref"]), + new RegExp(commit), + ); + }, +); test("pin upgrades publish children before parent locks and resume without republishing completed nodes", async (context) => { const workbench = await fs.mkdtemp(path.join(os.tmpdir(), "qx-upgrade-test-")); - context.after(() => fs.rm(workbench, {recursive: true, force: true})); - const from = "a".repeat(40), to = "b".repeat(40), framework = "c".repeat(40); - const sources = [{kind: "workspace" as const, directory: "root", source: {repository: "https://example.test/workspace.git", commit: from}}, - {kind: "interface" as const, directory: "resources/Named", source: {repository: "https://example.test/named.git", commit: from}}]; + context.after(() => fs.rm(workbench, { recursive: true, force: true })); + const from = "a".repeat(40), + to = "b".repeat(40), + framework = "c".repeat(40); + const sources = [ + { + kind: "workspace" as const, + directory: "root", + source: { repository: "https://example.test/workspace.git", commit: from }, + }, + { + kind: "interface" as const, + directory: "resources/Named", + source: { repository: "https://example.test/named.git", commit: from }, + }, + ]; const lock = `quixos-lock version 1 { quixos source { repository "https://example.test/quixos.git"; commit "${framework}"; }`; for (const node of sources) { const directory = path.join(workbench, node.directory); - await fs.mkdir(directory, {recursive: true}); + await fs.mkdir(directory, { recursive: true }); await execFile("git", ["-C", directory, "init"]); await execFile("git", ["-C", directory, "remote", "add", "origin", node.source.repository]); await fs.writeFile(path.join(directory, ".gitignore"), ".quixos/\n"); - await fs.writeFile(path.join(directory, "quixos.lock"), lock + (node.kind === "workspace" ? ` interface Named source { repository "${sources[1].source.repository}"; commit "${from}"; }` : "") + " }"); + await fs.writeFile( + path.join(directory, "quixos.lock"), + lock + + (node.kind === "workspace" + ? ` interface Named source { repository "${sources[1].source.repository}"; commit "${from}"; }` + : "") + + " }", + ); } - await fs.writeFile(path.join(workbench, "resources/Named/interface.qx"), 'interface Named id "interface:named" revision "interface:named@1" { value name id "member:name" : string { get id "op:get"; } }'); - await fs.writeFile(path.join(workbench, "root/workspace.qx"), `workspace W id "workspace:w" revision "workspace:w@1" commit "${from}" { import interface Named; atom A id "atom:a"; }`); + await fs.writeFile( + path.join(workbench, "resources/Named/interface.qx"), + 'interface Named id "interface:named" revision "interface:named@1" { value name id "member:name" : string { get id "op:get"; } }', + ); + await fs.writeFile( + path.join(workbench, "root/workspace.qx"), + `workspace W id "workspace:w" revision "workspace:w@1" commit "${from}" { import interface Named; atom A id "atom:a"; }`, + ); const snapshotMap = path.join(workbench, "snapshots.json"); - await fs.writeFile(snapshotMap, JSON.stringify({resources: [{kind: "interface", repository: sources[1].source.repository, commit: to, directory: path.join(workbench, "resources/Named")}]})); + await fs.writeFile( + snapshotMap, + JSON.stringify({ + resources: [ + { + kind: "interface", + repository: sources[1].source.repository, + commit: to, + directory: path.join(workbench, "resources/Named"), + }, + ], + }), + ); const oldMap = process.env.QUIXOS_SNAPSHOT_MAP; process.env.QUIXOS_SNAPSHOT_MAP = snapshotMap; - context.after(() => {if (oldMap === undefined) delete process.env.QUIXOS_SNAPSHOT_MAP; else process.env.QUIXOS_SNAPSHOT_MAP = oldMap;}); - const plan = await planPinUpgrades(workbench, {nodes: sources}); - await fs.mkdir(path.join(workbench, ".quixos"), {recursive: true}); - await fs.writeFile(path.join(workbench, ".quixos/resource-graph.json"), JSON.stringify({resources: [{...sources[1], source: {resolver: "git", ...sources[1].source}}]})); - assert.deepEqual(plan.nodes.map((node) => node.directory), ["resources/Named", "root"]); + context.after(() => { + if (oldMap === undefined) delete process.env.QUIXOS_SNAPSHOT_MAP; + else process.env.QUIXOS_SNAPSHOT_MAP = oldMap; + }); + const plan = await planPinUpgrades(workbench, { nodes: sources }); + await fs.mkdir(path.join(workbench, ".quixos"), { recursive: true }); + await fs.writeFile( + path.join(workbench, ".quixos/resource-graph.json"), + JSON.stringify({ resources: [{ ...sources[1], source: { resolver: "git", ...sources[1].source } }] }), + ); + assert.deepEqual( + plan.nodes.map((node) => node.directory), + ["resources/Named", "root"], + ); let fail = true; const published: string[] = []; const effects: UpgradeEffects = { - check: async (node) => {if (node.kind === "workspace" && fail) throw new Error("refactor required");}, + check: async (node) => { + if (node.kind === "workspace" && fail) throw new Error("refactor required"); + }, snapshot: async () => to, - publish: async (root) => {published.push(path.relative(workbench, root));}, + publish: async (root) => { + published.push(path.relative(workbench, root)); + }, }; await assert.rejects(() => applyPinUpgrades(plan, undefined, effects), /refactor required/); assert.deepEqual(published, ["resources/Named"]); assert.match(await fs.readFile(path.join(workbench, "root/quixos.lock"), "utf8"), new RegExp(to)); - const id = (await fs.readdir(path.join(workbench, ".quixos/upgrades"))).find((name) => name.endsWith(".json"))!.slice(0, -5); + const id = (await fs.readdir(path.join(workbench, ".quixos/upgrades"))) + .find((name) => name.endsWith(".json"))! + .slice(0, -5); fail = false; await fs.appendFile(path.join(workbench, "root/workspace.qx"), "\n// explicit refactor\n"); await assert.rejects(() => applyPinUpgrades(plan, id, effects), /accept-edits/); - const result = await applyPinUpgrades(plan, id, effects, {acceptEdits: true}); + const result = await applyPinUpgrades(plan, id, effects, { acceptEdits: true }); assert.equal(result.activated, false); assert.deepEqual(published, ["resources/Named", "root"]); const graph = JSON.parse(await fs.readFile(path.join(workbench, ".quixos/resource-graph.json"), "utf8")); assert.equal(graph.resources.length, 1); assert.equal(graph.resources[0].source.commit, to); - const next = await planPinUpgrades(workbench, {nodes: [sources[0], {...sources[1], source: graph.resources[0].source}]}); + const next = await planPinUpgrades(workbench, { + nodes: [sources[0], { ...sources[1], source: graph.resources[0].source }], + }); assert.deepEqual(next.nodes.find((node) => node.kind === "workspace")!.dependencies, ["resources/Named"]); }); diff --git a/test/qx-source.test.ts b/test/qx-source.test.ts index 1a974ef..9b9d2db 100644 --- a/test/qx-source.test.ts +++ b/test/qx-source.test.ts @@ -3,15 +3,30 @@ import test from "node:test"; import { mkdtemp, writeFile, readFile, rm, symlink } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import { parseQx, formatQx, lintQx, applySourceEdits, addWorkspaceImport, walkSyntax, - resolveQxSources, readQxSource, compileCapabilitySource, scaffoldAtom, compileWorkspaceRepository } from "../src/capability-language/index.js"; +import { + parseQx, + formatQx, + lintQx, + applySourceEdits, + addWorkspaceImport, + walkSyntax, + resolveQxSources, + readQxSource, + compileCapabilitySource, + scaffoldAtom, + compileWorkspaceRepository, +} from "../src/capability-language/index.js"; const workspace = `workspace Test id "w" revision "w@1" commit "${"1".repeat(40)}" {\n// keep 🐈 comment\n}\n`; test("lint reports invalid and redundant imports without resolving repositories", () => { const source = workspace.replace("}\n", 'import "a.qx"; import "a.qx"; import "../bad.qx";\n}\n'); - assert.deepEqual(lintQx(source).map((entry) => [entry.code, entry.severity]), [ - ["duplicate-source-import", "warning"], ["invalid-source-import", "error"], - ]); + assert.deepEqual( + lintQx(source).map((entry) => [entry.code, entry.severity]), + [ + ["duplicate-source-import", "warning"], + ["invalid-source-import", "error"], + ], + ); }); test("lossless tokens, UTF-16 ranges, and safe source edits", () => { const text = workspace.replace("}\n", 'atom Cat id "🐈";\n}\n'); @@ -20,8 +35,18 @@ test("lossless tokens, UTF-16 ranges, and safe source edits", () => { assert.equal(parsed.tokens.map((token) => token.text).join(""), text); const atom = [...walkSyntax(parsed.root)].find((node) => node.kind === "atomDecl")!; assert.equal(text.slice(atom.start, atom.end), 'atom Cat id "🐈";'); - assert.equal(applySourceEdits(text, [{ ...atom, text: 'atom Dog id "dog";' }]), text.replace('atom Cat id "🐈";', 'atom Dog id "dog";')); - assert.throws(() => applySourceEdits(text, [{ start: 0, end: 4, text: "" }, { start: 3, end: 7, text: "" }]), /overlapping/); + assert.equal( + applySourceEdits(text, [{ ...atom, text: 'atom Dog id "dog";' }]), + text.replace('atom Cat id "🐈";', 'atom Dog id "dog";'), + ); + assert.throws( + () => + applySourceEdits(text, [ + { start: 0, end: 4, text: "" }, + { start: 3, end: 7, text: "" }, + ]), + /overlapping/, + ); assert.throws(() => applySourceEdits(text, [{ start: -1, end: 0, text: "" }]), /Invalid/); }); test("formatting preserves comments and strings, is idempotent, and rejects invalid source", () => { @@ -36,10 +61,12 @@ test("imports preserve root text, deduplicate diamonds, reject cycles, and compi const root = addWorkspaceImport(addWorkspaceImport(workspace, "a.qx"), "b.qx"); assert.equal(addWorkspaceImport(root, "a.qx"), root); assert.match(root, /keep 🐈 comment/); - const files: Record = { "workspace.qx": root, + const files: Record = { + "workspace.qx": root, "a.qx": 'fragment { import "shared.qx"; atom A id "a"; }', "b.qx": 'fragment { import "shared.qx"; atom B id "b"; }', - "shared.qx": 'fragment { atom Shared id "shared"; }' }; + "shared.qx": 'fragment { atom Shared id "shared"; }', + }; const result = await resolveQxSources(async (name) => files[name]!); assert.equal(result.sourceFiles.length, 4); const compiled = compileCapabilitySource(result.source); @@ -49,15 +76,23 @@ test("imports preserve root text, deduplicate diamonds, reject cycles, and compi assert.equal(unresolved.ok, false); assert.match(JSON.stringify(unresolved.diagnostics), /unresolved-source-import/); files["shared.qx"] = 'fragment { import "a.qx"; }'; - await assert.rejects(resolveQxSources(async (name) => files[name]!), /cycle/); + await assert.rejects( + resolveQxSources(async (name) => files[name]!), + /cycle/, + ); assert.throws(() => addWorkspaceImport(workspace, "../x.qx"), /Invalid/); }); test("repository scaffolding validates before writing and refuses duplicates and symlinks", async (t) => { const root = await mkdtemp(path.join(os.tmpdir(), "qx-scaffold-")); t.after(() => rm(root, { recursive: true, force: true })); await writeFile(path.join(root, "workspace.qx"), workspace); - await writeFile(path.join(root, "quixos.lock"), `quixos-lock version 1 { quixos source { repository "https://example.test/q.git"; commit "${"1".repeat(40)}"; } }`); - const resolveResource = async (): Promise => { throw new Error("unexpected resource"); }; + await writeFile( + path.join(root, "quixos.lock"), + `quixos-lock version 1 { quixos source { repository "https://example.test/q.git"; commit "${"1".repeat(40)}"; } }`, + ); + const resolveResource = async (): Promise => { + throw new Error("unexpected resource"); + }; const options = { root, name: "Cat", id: "cat", write: false, resolveResource }; await scaffoldAtom(options); assert.equal(await readFile(path.join(root, "workspace.qx"), "utf8"), workspace); @@ -72,8 +107,10 @@ test("repository scaffolding validates before writing and refuses duplicates and await assert.rejects(readQxSource(root, "link.qx"), /ordinary files/); }); test("lowering diagnostics map to the imported file", async () => { - const files: Record = { "workspace.qx": addWorkspaceImport(workspace, "bad.qx"), - "bad.qx": 'fragment {\n conform Missing as Nope {}\n}' }; + const files: Record = { + "workspace.qx": addWorkspaceImport(workspace, "bad.qx"), + "bad.qx": "fragment {\n conform Missing as Nope {}\n}", + }; const sources = await resolveQxSources(async (name) => files[name]!); const compiled = compileCapabilitySource(sources.source); assert.equal(compiled.ok, false); diff --git a/test/resource-lock.test.ts b/test/resource-lock.test.ts index 79aed4a..31bc3c2 100644 --- a/test/resource-lock.test.ts +++ b/test/resource-lock.test.ts @@ -141,25 +141,34 @@ test("resolves root-relative lock fragments into one deterministic resource clos } }`; const sources = new Map([ - ["locks/web-studio.lock", `quixos-lock fragment version 1 { + [ + "locks/web-studio.lock", + `quixos-lock fragment version 1 { import "locks/shared.lock"; interface Placeable source { repository "https://repos.example/alice/interface-placeable.git"; commit "${namedCommit}"; } - }`], - ["locks/shared.lock", `quixos-lock fragment version 1 { + }`, + ], + [ + "locks/shared.lock", + `quixos-lock fragment version 1 { interface Named source { repository "https://repos.example/alice/interface-named.git"; commit "${namedCommit}"; } - }`], - ["locks/domain.lock", `quixos-lock fragment version 1 { + }`, + ], + [ + "locks/domain.lock", + `quixos-lock fragment version 1 { package TodoRuntime source { repository "https://repos.example/alice/package-todo.git"; commit "${packageCommit}"; } - }`], + }`, + ], ]); const result = await resolveQuixosLock(root, async (relativePath) => { const source = sources.get(relativePath); @@ -174,12 +183,15 @@ test("resolves root-relative lock fragments into one deterministic resource clos "locks/shared.lock", "locks/domain.lock", ]); - assert.deepEqual(result.lock.resources.map(({ kind, binding }) => ({ kind, binding })), [ - { kind: "package", binding: "RootRuntime" }, - { kind: "interface", binding: "Placeable" }, - { kind: "interface", binding: "Named" }, - { kind: "package", binding: "TodoRuntime" }, - ]); + assert.deepEqual( + result.lock.resources.map(({ kind, binding }) => ({ kind, binding })), + [ + { kind: "package", binding: "RootRuntime" }, + { kind: "interface", binding: "Placeable" }, + { kind: "interface", binding: "Named" }, + { kind: "package", binding: "TodoRuntime" }, + ], + ); }); test("lock fragments format canonically and cannot redeclare the Quixos source", () => { @@ -187,35 +199,43 @@ test("lock fragments format canonically and cannot redeclare the Quixos source", kind: "fragment" as const, formatVersion: 1 as const, imports: ["locks/shared.lock"], - resources: [{ - kind: "interface" as const, - binding: "Named", - source: { - resolver: "git" as const, - repository: "https://repos.example/alice/interface-named.git", - commit: namedCommit, + resources: [ + { + kind: "interface" as const, + binding: "Named", + source: { + resolver: "git" as const, + repository: "https://repos.example/alice/interface-named.git", + commit: namedCommit, + }, }, - }], + ], }; assert.deepEqual(parseQuixosLockDocument(formatQuixosLockDocument(document)), { ok: true, document, diagnostics: [], }); - const invalid = parseQuixosLockDocument(`quixos-lock fragment version 1 { + const invalid = parseQuixosLockDocument( + `quixos-lock fragment version 1 { quixos source { repository "https://gitea.example/quixos/quixos.git"; commit "${quixosCommit}"; } - }`, "bad.lock"); + }`, + "bad.lock", + ); assert.equal(invalid.ok, false); if (!invalid.ok) assert.equal(invalid.diagnostics[0]?.code, "fragment-has-quixos-source"); }); test("lock imports reject traversal, cycles, root documents, and cross-file binding collisions", async () => { - const invalidPath = parseQuixosLockDocument(`quixos-lock fragment version 1 { + const invalidPath = parseQuixosLockDocument( + `quixos-lock fragment version 1 { import "../outside.lock"; - }`, "bad-path.lock"); + }`, + "bad-path.lock", + ); assert.equal(invalidPath.ok, false); if (!invalidPath.ok) assert.equal(invalidPath.diagnostics[0]?.code, "invalid-import-path"); @@ -232,24 +252,26 @@ test("lock imports reject traversal, cycles, root documents, and cross-file bind } }`; const sources = new Map([ - ["a.lock", `quixos-lock fragment version 1 { + [ + "a.lock", + `quixos-lock fragment version 1 { import "b.lock"; interface Named source { repository "https://repos.example/alice/interface-named-copy.git"; commit "${namedCommit}"; } - }`], + }`, + ], ["b.lock", `quixos-lock fragment version 1 { import "a.lock"; }`], ["root-again.lock", root], ]); const result = await resolveQuixosLock(root, async (relativePath) => sources.get(relativePath) ?? ""); assert.equal(result.ok, false); if (!result.ok) { - assert.deepEqual(new Set(result.diagnostics.map(({ code }) => code)), new Set([ - "duplicate-resource-binding", - "import-cycle", - "imported-root-lock", - ])); + assert.deepEqual( + new Set(result.diagnostics.map(({ code }) => code)), + new Set(["duplicate-resource-binding", "import-cycle", "imported-root-lock"]), + ); } }); @@ -260,13 +282,16 @@ test("file loading rejects a symlink in any import path component", async (conte await mkdir(outside); await writeFile(path.join(outside, "fragment.lock"), "quixos-lock fragment version 1 {}\n"); await symlink(outside, path.join(directory, "linked"), "dir"); - await writeFile(path.join(directory, "quixos.lock"), `quixos-lock version 1 { + await writeFile( + path.join(directory, "quixos.lock"), + `quixos-lock version 1 { quixos source { repository "https://gitea.example/quixos/quixos.git"; commit "${quixosCommit}"; } import "linked/fragment.lock"; - }`); + }`, + ); const result = await loadQuixosLock(path.join(directory, "quixos.lock")); assert.equal(result.ok, false); diff --git a/test/scaffold-recipes.test.ts b/test/scaffold-recipes.test.ts index befd92f..f933301 100644 --- a/test/scaffold-recipes.test.ts +++ b/test/scaffold-recipes.test.ts @@ -3,46 +3,85 @@ import assert from "node:assert/strict"; import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import {execFile as callback} from "node:child_process"; -import {promisify} from "node:util"; -import {scaffoldRecipe} from "../src/capability-language/scaffold-recipes.js"; -import {planStructure, applyStructure} from "../src/capability-language/structural-plan.js"; -import {contentDigest} from "../src/capability-model/evolution.js"; -import {sealMigrations} from "../src/capability-language/migration-seal.js"; -import {reactPlatformTypes} from "../src/bindings/react-platform.js"; +import { execFile as callback } from "node:child_process"; +import { promisify } from "node:util"; +import { scaffoldRecipe } from "../src/capability-language/scaffold-recipes.js"; +import { planStructure, applyStructure } from "../src/capability-language/structural-plan.js"; +import { contentDigest } from "../src/capability-model/evolution.js"; +import { sealMigrations } from "../src/capability-language/migration-seal.js"; +import { reactPlatformTypes } from "../src/bindings/react-platform.js"; const execFile = promisify(callback); test("React preset applies its browser build script and shared-platform imports", async (context) => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "qx-react-recipe-")); - context.after(() => fs.rm(root, {recursive: true, force: true})); + context.after(() => fs.rm(root, { recursive: true, force: true })); await execFile("git", ["-C", root, "init"]); - const source = {repository: "https://example.test/react.git", commit: "a".repeat(40)}; - const request = await scaffoldRecipe(root, "package", {source, name: "React", id: "package:react", revision: "package:react@1", template: "typescript-react", tools: {quixos: source, protocol: source, helpers: source, sdk: source}}); + const source = { repository: "https://example.test/react.git", commit: "a".repeat(40) }; + const request = await scaffoldRecipe(root, "package", { + source, + name: "React", + id: "package:react", + revision: "package:react@1", + template: "typescript-react", + tools: { quixos: source, protocol: source, helpers: source, sdk: source }, + }); await applyStructure(await planStructure(root, request)); assert.match(await fs.readFile(path.join(root, "src/impl/sourceGet.ts"), "utf8"), /component.js\?browser-source/); assert.match(await fs.readFile(path.join(root, "flake.nix"), "utf8"), /browserSources = true/); assert.equal(await fs.readFile(path.join(root, "src/gen/web-studio-react-runtime.d.ts"), "utf8"), reactPlatformTypes); assert.match(await fs.readFile(path.join(root, "src/browser-assets.d.ts"), "utf8"), /declare module "\*\.css"/); - assert.equal(JSON.parse(await fs.readFile(path.join(root, "quixos.check.json"), "utf8")).options.messages["org.quixos.web-studio.ReactProps"].export, "opaqueReactPropsBinding"); + assert.equal( + JSON.parse(await fs.readFile(path.join(root, "quixos.check.json"), "utf8")).options.messages[ + "org.quixos.web-studio.ReactProps" + ].export, + "opaqueReactPropsBinding", + ); await assert.rejects(fs.access(path.join(root, "quixos.scaffold.json"))); - assert.equal(JSON.parse(await fs.readFile(path.join(root, "tsconfig.json"), "utf8")).compilerOptions.jsx, "react-jsx"); + assert.equal( + JSON.parse(await fs.readFile(path.join(root, "tsconfig.json"), "utf8")).compilerOptions.jsx, + "react-jsx", + ); }); test("imperative scaffolds preserve authored wiring; migration sealing is explicit and separate", async (context) => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "qx-recipes-")); - context.after(() => fs.rm(root, {recursive: true, force: true})); + context.after(() => fs.rm(root, { recursive: true, force: true })); await execFile("git", ["-C", root, "init"]); await fs.writeFile(path.join(root, ".gitignore"), ".quixos/\n"); - const source = {repository: "https://example.test/chess.git", commit: "a".repeat(40)}; - const base = {source, directory: "packages/Chess"}; - const apply = async (command: Parameters[1], input: Parameters[2]) => applyStructure(await planStructure(root, await scaffoldRecipe(root, command, input))); - await apply("package", {...base, name: "Chess", id: "package:chess", revision: "package:chess@1", tools: {quixos: source, protocol: source, helpers: source, sdk: source}}); - await apply("function", {...base, name: "play", id: "export:play"}); + const source = { repository: "https://example.test/chess.git", commit: "a".repeat(40) }; + const base = { source, directory: "packages/Chess" }; + const apply = async (command: Parameters[1], input: Parameters[2]) => + applyStructure(await planStructure(root, await scaffoldRecipe(root, command, input))); + await apply("package", { + ...base, + name: "Chess", + id: "package:chess", + revision: "package:chess@1", + tools: { quixos: source, protocol: source, helpers: source, sdk: source }, + }); + await apply("function", { ...base, name: "play", id: "export:play" }); const filename = path.join(root, base.directory, "src/impl/play.ts"); - const edited = 'import type {Implementation} from "../gen/qx.js";\nexport const handler: Implementation["play"] = async () => null;\n'; + const edited = + 'import type {Implementation} from "../gen/qx.js";\nexport const handler: Implementation["play"] = async () => null;\n'; await fs.writeFile(filename, edited); - const old = {version: 1}, next = {version: 2}, from = contentDigest(old), to = contentDigest(next); - await apply("migration", {...base, name: "upgrade", id: "export:upgrade", migration: {id: "upgrade-v2", scopeId: "board", from, to, predecessors: [], ports: [], contracts: {[from]: old, [to]: next}}}); + const old = { version: 1 }, + next = { version: 2 }, + from = contentDigest(old), + to = contentDigest(next); + await apply("migration", { + ...base, + name: "upgrade", + id: "export:upgrade", + migration: { + id: "upgrade-v2", + scopeId: "board", + from, + to, + predecessors: [], + ports: [], + contracts: { [from]: old, [to]: next }, + }, + }); const migrationFile = path.join(root, base.directory, "src/migrations/upgrade.ts"); await fs.appendFile(migrationFile, "\n// authored migration change\n"); await sealMigrations(path.join(root, base.directory)); @@ -50,18 +89,38 @@ test("imperative scaffolds preserve authored wiring; migration sealing is explic const catalog = JSON.parse(await fs.readFile(path.join(root, base.directory, "quixos.migrations.json"), "utf8")); assert.equal(catalog.migrations[0].implementation.digest, contentDigest(await fs.readFile(migrationFile, "utf8"))); assert.match(await fs.readFile(path.join(root, base.directory, "src/migrate.ts"), "utf8"), /export:upgrade/); - await assert.rejects(() => apply("function", {...base, name: "play", id: "export:play"}), /unique/); + await assert.rejects(() => apply("function", { ...base, name: "play", id: "export:play" }), /unique/); const declarations = path.join(root, base.directory, "package.qx"); - await fs.writeFile(declarations, (await fs.readFile(declarations, "utf8")).replace(/}\s*$/, ' function authored id "export:authored" : unit -> unit;\n}\n')); + await fs.writeFile( + declarations, + (await fs.readFile(declarations, "utf8")).replace( + /}\s*$/, + ' function authored id "export:authored" : unit -> unit;\n}\n', + ), + ); const server = path.join(root, base.directory, "src/server.ts"); await fs.appendFile(server, "\n// authored comment must survive\n"); - await apply("function", {...base, name: "another", id: "export:another"}); + await apply("function", { ...base, name: "another", id: "export:another" }); assert.match(await fs.readFile(server, "utf8"), /authored comment must survive/); await assert.rejects(() => apply("refresh", base), /removed/); await assert.rejects(fs.access(path.join(root, base.directory, "src/impl/authored.ts"))); assert.equal(await fs.readFile(filename, "utf8"), edited); - await planStructure(root, {kind: "package", source, resourceRoot: base.directory, validation: "syntax", files: [{ - file: `${base.directory}/package.qx`, edits: [{operation: "replace", target: {kind: "packageResourceDecl", id: "package:chess"}, - source: 'package Other id "package:other" revision "package:other@1" {}'}], - }]}); + await planStructure(root, { + kind: "package", + source, + resourceRoot: base.directory, + validation: "syntax", + files: [ + { + file: `${base.directory}/package.qx`, + edits: [ + { + operation: "replace", + target: { kind: "packageResourceDecl", id: "package:chess" }, + source: 'package Other id "package:other" revision "package:other@1" {}', + }, + ], + }, + ], + }); }); diff --git a/test/structural-edits.test.ts b/test/structural-edits.test.ts index 4aef125..a8e582d 100644 --- a/test/structural-edits.test.ts +++ b/test/structural-edits.test.ts @@ -1,9 +1,16 @@ import test from "node:test"; import assert from "node:assert/strict"; -import { editStructure, scaffoldResourceSource, instantiateWorkspaceIdentity } from "../src/capability-language/structural-edits.js"; +import { + editStructure, + scaffoldResourceSource, + instantiateWorkspaceIdentity, +} from "../src/capability-language/structural-edits.js"; test("template identity binding changes only the workspace header and is idempotent", () => { - const source = '// workspace fake id "do-not-touch"\nworkspace Todo id "workspace:todo" revision "workspace:todo@1" commit "' + "a".repeat(40) + '" { atom Task id "atom:task"; }'; + const source = + '// workspace fake id "do-not-touch"\nworkspace Todo id "workspace:todo" revision "workspace:todo@1" commit "' + + "a".repeat(40) + + '" { atom Task id "atom:task"; }'; const id = "00000000-0000-0000-0000-000000000123"; const bound = instantiateWorkspaceIdentity(source, id); assert.match(bound, /atom Task id "atom:task"/); @@ -16,41 +23,81 @@ test("template identity binding changes only the workspace header and is idempot test("package scaffolding and function edits preserve surrounding source and use exact selectors", () => { const source = `// 🧭 resource comment\n${scaffoldResourceSource("package", "Chess", "package:chess", "package:chess@1")}`; const functionSource = 'function evaluate id "export:evaluate" : string -> string;'; - const appended = editStructure(source, {operation: "append", parent: {kind: "packageResourceDecl", id: "package:chess"}, source: functionSource}); + const appended = editStructure(source, { + operation: "append", + parent: { kind: "packageResourceDecl", id: "package:chess" }, + source: functionSource, + }); assert.ok(appended.startsWith("// 🧭 resource comment\n")); assert.ok(appended.includes(functionSource)); - const replaced = editStructure(appended, {operation: "replace", target: {kind: "packageFunctionExport", id: "export:evaluate"}, source: 'function evaluate id "export:evaluate" : unit -> string;'}); + const replaced = editStructure(appended, { + operation: "replace", + target: { kind: "packageFunctionExport", id: "export:evaluate" }, + source: 'function evaluate id "export:evaluate" : unit -> string;', + }); assert.ok(replaced.includes(": unit -> string;")); - assert.ok(!editStructure(replaced, {operation: "remove", target: {kind: "packageFunctionExport", id: "export:evaluate"}}).includes("evaluate")); - assert.throws(() => editStructure(source, {operation: "remove", target: {kind: "packageResourceDecl", id: "package:chess@1"}}), /exactly once/); - assert.throws(() => editStructure(source, {operation: "append", parent: {kind: "packageResourceDecl"}, source: "not valid QX"}), /Invalid structural/); + assert.ok( + !editStructure(replaced, { + operation: "remove", + target: { kind: "packageFunctionExport", id: "export:evaluate" }, + }).includes("evaluate"), + ); + assert.throws( + () => + editStructure(source, { operation: "remove", target: { kind: "packageResourceDecl", id: "package:chess@1" } }), + /exactly once/, + ); + assert.throws( + () => + editStructure(source, { operation: "append", parent: { kind: "packageResourceDecl" }, source: "not valid QX" }), + /Invalid structural/, + ); const prefixed = `import interface Board;\nexternal atom Game id "atom:game";\n${source}`; - const replacedPackage = editStructure(prefixed, {operation: "replace", target: {kind: "packageResourceDecl", id: "package:chess"}, source: scaffoldResourceSource("package", "Chess", "package:chess", "package:chess@2")}); + const replacedPackage = editStructure(prefixed, { + operation: "replace", + target: { kind: "packageResourceDecl", id: "package:chess" }, + source: scaffoldResourceSource("package", "Chess", "package:chess", "package:chess@2"), + }); assert.ok(replacedPackage.startsWith('import interface Board;\nexternal atom Game id "atom:game";')); }); test("dependency scaffolding validates exact sources and preserves unrelated lock comments", () => { const source = `// 🧭 lock\nquixos-lock version 1 {\n quixos source { repository "https://example.test/quixos.git"; commit "${"a".repeat(40)}"; }\n // retained comment\n}\n`; - const dependency = {operation: "dependency" as const, kind: "package" as const, name: "Chess", source: {repository: "https://example.test/chess.git", commit: "b".repeat(40)}}; + const dependency = { + operation: "dependency" as const, + kind: "package" as const, + name: "Chess", + source: { repository: "https://example.test/chess.git", commit: "b".repeat(40) }, + }; const appended = editStructure(source, dependency); assert.ok(appended.includes("// retained comment")); assert.ok(appended.startsWith("// 🧭 lock\n")); - assert.throws(() => editStructure(source, {...dependency, source: {...dependency.source, commit: "main"}}), /Invalid dependency/); - const changed = editStructure(appended, {...dependency, source: {...dependency.source, commit: "c".repeat(40)}}); + assert.throws( + () => editStructure(source, { ...dependency, source: { ...dependency.source, commit: "main" } }), + /Invalid dependency/, + ); + const changed = editStructure(appended, { ...dependency, source: { ...dependency.source, commit: "c".repeat(40) } }); assert.ok(!changed.includes("b".repeat(40))); - assert.ok(!editStructure(changed, {...dependency, source: null}).includes("package Chess")); + assert.ok(!editStructure(changed, { ...dependency, source: null }).includes("package Chess")); }); test("conformance enrollment, major edits, imports, and private attachment removal preserve valid structure", () => { const source = `fragment { conform Game as Playable { private state Board id "slot:board" on Game : string policy optimistic-register; } }`; - const selector = {kind: "conformanceDecl", names: ["Game", "Playable"]}; - const enrolled = editStructure(source, {operation: "conformance-id", target: selector, id: "conformance:playable"}); + const selector = { kind: "conformanceDecl", names: ["Game", "Playable"] }; + const enrolled = editStructure(source, { operation: "conformance-id", target: selector, id: "conformance:playable" }); assert.ok(enrolled.includes('as Playable id "conformance:playable"')); - const major = editStructure(enrolled, {operation: "semantic-major", target: {kind: "conformanceDecl", id: "conformance:playable"}, major: 2}); + const major = editStructure(enrolled, { + operation: "semantic-major", + target: { kind: "conformanceDecl", id: "conformance:playable" }, + major: 2, + }); assert.ok(major.includes("semantic-major 2")); - assert.throws(() => editStructure(major, {operation: "conformance-id", target: selector, id: "different"}), /Cannot change/); - const removed = editStructure(major, {operation: "remove", target: {kind: "stateDecl", id: "slot:board"}}); + assert.throws( + () => editStructure(major, { operation: "conformance-id", target: selector, id: "different" }), + /Cannot change/, + ); + const removed = editStructure(major, { operation: "remove", target: { kind: "stateDecl", id: "slot:board" } }); assert.ok(!removed.includes("private")); - const imported = editStructure(removed, {operation: "import", kind: "interface", name: "Playable"}); - assert.equal(editStructure(imported, {operation: "import", kind: "interface", name: "Playable"}), imported); + const imported = editStructure(removed, { operation: "import", kind: "interface", name: "Playable" }); + assert.equal(editStructure(imported, { operation: "import", kind: "interface", name: "Playable" }), imported); }); diff --git a/test/structural-plan.test.ts b/test/structural-plan.test.ts index abfec34..d57c6a5 100644 --- a/test/structural-plan.test.ts +++ b/test/structural-plan.test.ts @@ -5,18 +5,34 @@ import os from "node:os"; import path from "node:path"; import { promisify } from "node:util"; import { execFile as callback } from "node:child_process"; -import { planStructure, applyStructure, resumeStructure, type StructuralRequest } from "../src/capability-language/structural-plan.js"; +import { + planStructure, + applyStructure, + resumeStructure, + type StructuralRequest, +} from "../src/capability-language/structural-plan.js"; const execFile = promisify(callback); test("structural plans validate the graph, journal originals, and reject stale edits", async (context) => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "qx-structural-test-")); - context.after(() => fs.rm(root, {recursive: true, force: true})); + context.after(() => fs.rm(root, { recursive: true, force: true })); await execFile("git", ["-C", root, "init"]); await fs.writeFile(path.join(root, ".gitignore"), ".quixos/\n"); - await fs.writeFile(path.join(root, "quixos.lock"), `quixos-lock version 1 { quixos source { repository "https://example.test/quixos.git"; commit "${"a".repeat(40)}"; } }`); + await fs.writeFile( + path.join(root, "quixos.lock"), + `quixos-lock version 1 { quixos source { repository "https://example.test/quixos.git"; commit "${"a".repeat(40)}"; } }`, + ); const before = `workspace Test id "workspace:test" revision "workspace:test@1" commit "${"b".repeat(40)}" { atom Subject id "atom:subject"; }`; await fs.writeFile(path.join(root, "workspace.qx"), before); - const request: StructuralRequest = {kind: "workspace", files: [{file: "workspace.qx", edits: [{operation: "append", parent: {kind: "workspaceDecl"}, source: 'atom Game id "atom:game";'}]}]}; + const request: StructuralRequest = { + kind: "workspace", + files: [ + { + file: "workspace.qx", + edits: [{ operation: "append", parent: { kind: "workspaceDecl" }, source: 'atom Game id "atom:game";' }], + }, + ], + }; const plan = await planStructure(root, request); assert.equal(await fs.readFile(path.join(root, "workspace.qx"), "utf8"), before); await fs.writeFile(path.join(root, "workspace.qx"), `${before}\n// newer edit`); @@ -35,12 +51,14 @@ test("structural plans validate the graph, journal originals, and reject stale e assert.equal((await resumeStructure(root, applied.id)).phase, "complete"); await assert.rejects(() => planStructure(root, request), /Duplicate|duplicate/); // Cross-repository edits may temporarily refer to an unfinished provider. - const provisional: StructuralRequest = {kind: "workspace", validation: "syntax", files: [{file: "workspace.qx", edits: [ - {operation: "import", kind: "interface", name: "NotImplementedYet"}, - ]}]}; + const provisional: StructuralRequest = { + kind: "workspace", + validation: "syntax", + files: [{ file: "workspace.qx", edits: [{ operation: "import", kind: "interface", name: "NotImplementedYet" }] }], + }; const draft = await planStructure(root, provisional); assert.equal(draft.validation, "syntax"); await applyStructure(draft); assert.match(await fs.readFile(path.join(root, "workspace.qx"), "utf8"), /import interface NotImplementedYet/); - await assert.rejects(() => planStructure(root, {...provisional, validation: "resource-graph"})); + await assert.rejects(() => planStructure(root, { ...provisional, validation: "resource-graph" })); }); From 8f28ab1b937d6fabe5cfd3a6f96ad763fbb9bc89 Mon Sep 17 00:00:00 2001 From: "Timothy J. Aveni" Date: Tue, 15 Sep 2026 15:31:50 -0700 Subject: [PATCH 06/11] Add lightweight correctness linting and resolve formatting conflicts --- test/capability-language.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/capability-language.test.ts b/test/capability-language.test.ts index 9a598da..826cad3 100644 --- a/test/capability-language.test.ts +++ b/test/capability-language.test.ts @@ -148,7 +148,7 @@ test("interfaces can declare ordinary call operations", () => { test("state defaults accept recursive JSON values", () => { const source = capabilityFixtureSource.replace( - ' policy optimistic-register default "Untitled project";', + /policy optimistic-register default "Untitled project";/, ` policy optimistic-register default "Untitled project"; shared state ProjectMetadata id "slot:project:metadata" on Project : message "example.Metadata" policy optimistic-register default {"labels":["compiler","runtime"],"score":1.5,"enabled":true,"extra":null};`, From 1afc9feb94c0cb4c8470dbafa74480c9a01396f1 Mon Sep 17 00:00:00 2001 From: "Timothy J. Aveni" Date: Tue, 15 Sep 2026 17:27:38 -0700 Subject: [PATCH 07/11] Name optional component error notifications extraErrorAction --- src/bindings/react-platform.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bindings/react-platform.ts b/src/bindings/react-platform.ts index 0d3a997..09e8977 100644 --- a/src/bindings/react-platform.ts +++ b/src/bindings/react-platform.ts @@ -28,7 +28,8 @@ declare module "@quixos/web-studio-react-runtime" { fallback?: React.ReactNode; className?: string; style?: React.CSSProperties; - onError?: (error: Error) => void; + /** Optional extra action. The host still displays and reports errors by default. */ + extraErrorAction?: (error: Error) => void; }; export type ReactComponentImplementationProps = { camino: CaminoProps; @@ -45,7 +46,7 @@ declare module "@quixos/web-studio-react-runtime" { interfaceRevisionId: string, operationId: string, value?: unknown, - options?: {clientMutationId?: string}, + options?: {clientMutationId?: string; signal?: AbortSignal}, ) => Promise; export const h: typeof React.createElement; export const useLiveField: ( From e557a057d89af59b3fd664bf00550195020e203e Mon Sep 17 00:00:00 2001 From: "Timothy J. Aveni" Date: Tue, 15 Sep 2026 17:32:53 -0700 Subject: [PATCH 08/11] Mark first-party generated files with .gen; preserve third-party conventions --- .yarnrc.yml | 1 + README.md | 2 +- flake.nix | 4 ++-- src/bindings/cli.ts | 2 +- src/capability-language/scaffold-recipes.ts | 16 ++++++++-------- src/capability-language/tool-cli.ts | 5 +++-- test/scaffold-recipes.test.ts | 7 +++++-- yarn-project.nix => yarn-project.gen.nix | 0 8 files changed, 21 insertions(+), 16 deletions(-) rename yarn-project.nix => yarn-project.gen.nix (100%) diff --git a/.yarnrc.yml b/.yarnrc.yml index de9b346..42b6936 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1,3 +1,4 @@ +nixExprPath: yarn-project.gen.nix enableScripts: true generateDefaultNix: false diff --git a/README.md b/README.md index c71eedc..0cef1ea 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ fragment, so fragments do not become independent repositories or identities. Executable package metadata remains protobuf text format: ```sh -quixos-descriptor-check path/to/descriptor.quixos-package.txtpb +quixos-descriptor-check path/to/descriptor.quixos-package.gen.txtpb ``` Descriptors identify exact package revisions and exported runtime symbols. diff --git a/flake.nix b/flake.nix index 7a86d3d..f990f98 100644 --- a/flake.nix +++ b/flake.nix @@ -18,7 +18,7 @@ let pkgs = import nixpkgs { inherit system; }; nodejs = pkgs.nodejs_24; - quixos-protocol = (pkgs.callPackage ./yarn-project.nix { inherit nodejs; }) { + quixos-protocol = (pkgs.callPackage ./yarn-project.gen.nix { inherit nodejs; }) { src = pkgs.lib.cleanSource ./.; overrideAttrs = old: { nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ @@ -127,7 +127,7 @@ in { packages.default = quixos-protocol; - packages.inspector = (pkgs.callPackage ./yarn-project.nix { inherit nodejs; }) { + packages.inspector = (pkgs.callPackage ./yarn-project.gen.nix { inherit nodejs; }) { src = pkgs.lib.fileset.toSource { root = ./.; fileset = pkgs.lib.fileset.unions [ diff --git a/src/bindings/cli.ts b/src/bindings/cli.ts index df81397..86ccab9 100644 --- a/src/bindings/cli.ts +++ b/src/bindings/cli.ts @@ -12,7 +12,7 @@ const main = async () => { const generated = generateTypeScriptBindings(JSON.parse(await readFile(schema, "utf8")), revision, config); await writeFile(output, generated); if (config.messages?.["org.quixos.web-studio.ReactProps"]) { - await writeFile(path.join(path.dirname(output), "web-studio-react-runtime.d.ts"), reactPlatformTypes); + await writeFile(path.join(path.dirname(output), "web-studio-react-runtime.gen.d.ts"), reactPlatformTypes); } }; main().catch((error: unknown) => { diff --git a/src/capability-language/scaffold-recipes.ts b/src/capability-language/scaffold-recipes.ts index 7a3dd20..71bea7b 100644 --- a/src/capability-language/scaffold-recipes.ts +++ b/src/capability-language/scaffold-recipes.ts @@ -146,13 +146,13 @@ export const scaffoldRecipe = async ( ); create( "src/impl/sourceGet.ts", - `import componentSource from "../component.js?browser-source";\nimport type {Implementation} from "../gen/qx.js";\nexport const handler: Implementation["sourceGet"] = () => componentSource;\n`, + `import componentSource from "../component.js?browser-source";\nimport type {Implementation} from "../gen/qx.gen.js";\nexport const handler: Implementation["sourceGet"] = () => componentSource;\n`, ); create( "src/browser-assets.d.ts", `declare module "*?browser-source" { const source: string; export default source; }\ndeclare module "*.css" {}\n`, ); - create("src/gen/web-studio-react-runtime.d.ts", reactPlatformTypes); + create("src/gen/web-studio-react-runtime.gen.d.ts", reactPlatformTypes); } create(".gitignore", "node_modules/\ndist/\n.quixos/\nresult\n.yarn/install-state.gz\n"); create( @@ -177,7 +177,7 @@ export const scaffoldRecipe = async ( "quixos.check.json", json({ backend: "typescript", - bindingOutput: "src/gen/qx.ts", + bindingOutput: "src/gen/qx.gen.ts", ...(react ? { options: { @@ -204,7 +204,7 @@ export const scaffoldRecipe = async ( inherit inputs nixpkgs flake-utils; packageRoot = ./.; bundle = { entry = ${JSON.stringify(react ? "src/server.ts" : "dist/server.js")}; ${react ? "browserSources = true;" : ""} }; migrationEntrypoint = "dist/migrate.js"; - installServer = { libexecName = ${JSON.stringify(name.toLowerCase())}; descriptorPath = "descriptor.quixos-package.txtpb"; }; + installServer = { libexecName = ${JSON.stringify(name.toLowerCase())}; descriptorPath = "descriptor.quixos-package.gen.txtpb"; }; }; }\n`, ); @@ -282,7 +282,7 @@ export const scaffoldRecipe = async ( const implementation = command === "migration" ? `import type {MigrationContext} from "@quixos/camino-package-runtime";\nexport const handler = async (_context: MigrationContext): Promise => { throw new Error(${JSON.stringify(`Implement migration ${name}`)}); };\n` - : `import type {Implementation} from "../gen/qx.js";\nexport const handler: Implementation[${JSON.stringify(name)}] = ${derived ? '{kind: "derived", get: ' : ""}async (_context) => { throw new Error(${JSON.stringify(`Implement ${name}`)}); }${derived ? "}" : ""};\n`; + : `import type {Implementation} from "../gen/qx.gen.js";\nexport const handler: Implementation[${JSON.stringify(name)}] = ${derived ? '{kind: "derived", get: ' : ""}async (_context) => { throw new Error(${JSON.stringify(`Implement ${name}`)}); }${derived ? "}" : ""};\n`; create(file, implementation); packageModel.exports.push({ name, id: spec.id, file, ...(command === "migration" ? { migration: true } : {}) }); if (command === "migration") { @@ -333,7 +333,7 @@ export const scaffoldRecipe = async ( } else { create( "src/server.ts", - `import {servePackageRuntime} from "@quixos/camino-package-runtime";\nimport {createRuntime} from "./gen/qx.js";\n` + + `import {servePackageRuntime} from "@quixos/camino-package-runtime";\nimport {createRuntime} from "./gen/qx.gen.js";\n` + packageModel.exports .filter((entry) => !entry.migration) .map( @@ -364,7 +364,7 @@ export const scaffoldRecipe = async ( ); } generated( - "descriptor.quixos-package.txtpb", + "descriptor.quixos-package.gen.txtpb", `# Generated by qx-scaffold-v1\npackage_id: ${JSON.stringify(packageModel.id)}\npackage_revision_id: ${JSON.stringify(packageModel.revision)}\nruntime_protocol_version: "quixos-capabilities-v1"\n` + packageModel.exports .map( @@ -403,7 +403,7 @@ export const scaffoldRecipe = async ( } // A composed React recipe supplies its own modules and complete server. if (reactRecipe(spec)) { - for (const file of ["src/component.tsx", "src/impl/sourceGet.ts", "descriptor.quixos-package.txtpb"]) + for (const file of ["src/component.tsx", "src/impl/sourceGet.ts", "descriptor.quixos-package.gen.txtpb"]) if (!(file in spec.initialFiles)) { const index = files.findIndex((entry) => entry.file === prefix + file); if (index >= 0) files.splice(index, 1); diff --git a/src/capability-language/tool-cli.ts b/src/capability-language/tool-cli.ts index feae14a..b2c8329 100644 --- a/src/capability-language/tool-cli.ts +++ b/src/capability-language/tool-cli.ts @@ -437,6 +437,7 @@ const main = async () => { for (const [executable, args] of [ ["corepack", ["yarn", "plugin", "import", toolchain.nixifyPluginUrl]], ["corepack", ["yarn", "config", "set", "generateDefaultNix", "false"]], + ["corepack", ["yarn", "config", "set", "nixExprPath", "yarn-project.gen.nix"]], ["corepack", ["yarn", "config", "set", "individualNixPackaging", "true"]], ["corepack", ["yarn", "install"]], ] as const) { @@ -447,10 +448,10 @@ const main = async () => { ); } try { - await readFile(path.join(cwd, "yarn-project.nix")); + await readFile(path.join(cwd, "yarn-project.gen.nix")); } catch { throw new Error( - "Nixify did not generate yarn-project.nix. It skips repositories under the OS temporary directory; use an ordinary workspace checkout and retry installation.", + "Nixify did not generate yarn-project.gen.nix. It skips repositories under the OS temporary directory; use an ordinary workspace checkout and retry installation.", ); } await snapshotCommit(cwd); diff --git a/test/scaffold-recipes.test.ts b/test/scaffold-recipes.test.ts index f933301..56d02d6 100644 --- a/test/scaffold-recipes.test.ts +++ b/test/scaffold-recipes.test.ts @@ -28,7 +28,10 @@ test("React preset applies its browser build script and shared-platform imports" await applyStructure(await planStructure(root, request)); assert.match(await fs.readFile(path.join(root, "src/impl/sourceGet.ts"), "utf8"), /component.js\?browser-source/); assert.match(await fs.readFile(path.join(root, "flake.nix"), "utf8"), /browserSources = true/); - assert.equal(await fs.readFile(path.join(root, "src/gen/web-studio-react-runtime.d.ts"), "utf8"), reactPlatformTypes); + assert.equal( + await fs.readFile(path.join(root, "src/gen/web-studio-react-runtime.gen.d.ts"), "utf8"), + reactPlatformTypes, + ); assert.match(await fs.readFile(path.join(root, "src/browser-assets.d.ts"), "utf8"), /declare module "\*\.css"/); assert.equal( JSON.parse(await fs.readFile(path.join(root, "quixos.check.json"), "utf8")).options.messages[ @@ -62,7 +65,7 @@ test("imperative scaffolds preserve authored wiring; migration sealing is explic await apply("function", { ...base, name: "play", id: "export:play" }); const filename = path.join(root, base.directory, "src/impl/play.ts"); const edited = - 'import type {Implementation} from "../gen/qx.js";\nexport const handler: Implementation["play"] = async () => null;\n'; + 'import type {Implementation} from "../gen/qx.gen.js";\nexport const handler: Implementation["play"] = async () => null;\n'; await fs.writeFile(filename, edited); const old = { version: 1 }, next = { version: 2 }, diff --git a/yarn-project.nix b/yarn-project.gen.nix similarity index 100% rename from yarn-project.nix rename to yarn-project.gen.nix From 9358b5ed0e608e4a90b408f570ca007e373b372a Mon Sep 17 00:00:00 2001 From: "Timothy J. Aveni" Date: Tue, 15 Sep 2026 20:13:10 -0700 Subject: [PATCH 09/11] Revert generated-file renaming; retain formatter exclusions --- .yarnrc.yml | 1 - README.md | 2 +- flake.nix | 4 ++-- src/bindings/cli.ts | 2 +- src/capability-language/scaffold-recipes.ts | 16 ++++++++-------- src/capability-language/tool-cli.ts | 5 ++--- test/scaffold-recipes.test.ts | 7 ++----- yarn-project.gen.nix => yarn-project.nix | 0 8 files changed, 16 insertions(+), 21 deletions(-) rename yarn-project.gen.nix => yarn-project.nix (100%) diff --git a/.yarnrc.yml b/.yarnrc.yml index 42b6936..de9b346 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1,4 +1,3 @@ -nixExprPath: yarn-project.gen.nix enableScripts: true generateDefaultNix: false diff --git a/README.md b/README.md index 0cef1ea..c71eedc 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ fragment, so fragments do not become independent repositories or identities. Executable package metadata remains protobuf text format: ```sh -quixos-descriptor-check path/to/descriptor.quixos-package.gen.txtpb +quixos-descriptor-check path/to/descriptor.quixos-package.txtpb ``` Descriptors identify exact package revisions and exported runtime symbols. diff --git a/flake.nix b/flake.nix index f990f98..7a86d3d 100644 --- a/flake.nix +++ b/flake.nix @@ -18,7 +18,7 @@ let pkgs = import nixpkgs { inherit system; }; nodejs = pkgs.nodejs_24; - quixos-protocol = (pkgs.callPackage ./yarn-project.gen.nix { inherit nodejs; }) { + quixos-protocol = (pkgs.callPackage ./yarn-project.nix { inherit nodejs; }) { src = pkgs.lib.cleanSource ./.; overrideAttrs = old: { nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ @@ -127,7 +127,7 @@ in { packages.default = quixos-protocol; - packages.inspector = (pkgs.callPackage ./yarn-project.gen.nix { inherit nodejs; }) { + packages.inspector = (pkgs.callPackage ./yarn-project.nix { inherit nodejs; }) { src = pkgs.lib.fileset.toSource { root = ./.; fileset = pkgs.lib.fileset.unions [ diff --git a/src/bindings/cli.ts b/src/bindings/cli.ts index 86ccab9..df81397 100644 --- a/src/bindings/cli.ts +++ b/src/bindings/cli.ts @@ -12,7 +12,7 @@ const main = async () => { const generated = generateTypeScriptBindings(JSON.parse(await readFile(schema, "utf8")), revision, config); await writeFile(output, generated); if (config.messages?.["org.quixos.web-studio.ReactProps"]) { - await writeFile(path.join(path.dirname(output), "web-studio-react-runtime.gen.d.ts"), reactPlatformTypes); + await writeFile(path.join(path.dirname(output), "web-studio-react-runtime.d.ts"), reactPlatformTypes); } }; main().catch((error: unknown) => { diff --git a/src/capability-language/scaffold-recipes.ts b/src/capability-language/scaffold-recipes.ts index 71bea7b..7a3dd20 100644 --- a/src/capability-language/scaffold-recipes.ts +++ b/src/capability-language/scaffold-recipes.ts @@ -146,13 +146,13 @@ export const scaffoldRecipe = async ( ); create( "src/impl/sourceGet.ts", - `import componentSource from "../component.js?browser-source";\nimport type {Implementation} from "../gen/qx.gen.js";\nexport const handler: Implementation["sourceGet"] = () => componentSource;\n`, + `import componentSource from "../component.js?browser-source";\nimport type {Implementation} from "../gen/qx.js";\nexport const handler: Implementation["sourceGet"] = () => componentSource;\n`, ); create( "src/browser-assets.d.ts", `declare module "*?browser-source" { const source: string; export default source; }\ndeclare module "*.css" {}\n`, ); - create("src/gen/web-studio-react-runtime.gen.d.ts", reactPlatformTypes); + create("src/gen/web-studio-react-runtime.d.ts", reactPlatformTypes); } create(".gitignore", "node_modules/\ndist/\n.quixos/\nresult\n.yarn/install-state.gz\n"); create( @@ -177,7 +177,7 @@ export const scaffoldRecipe = async ( "quixos.check.json", json({ backend: "typescript", - bindingOutput: "src/gen/qx.gen.ts", + bindingOutput: "src/gen/qx.ts", ...(react ? { options: { @@ -204,7 +204,7 @@ export const scaffoldRecipe = async ( inherit inputs nixpkgs flake-utils; packageRoot = ./.; bundle = { entry = ${JSON.stringify(react ? "src/server.ts" : "dist/server.js")}; ${react ? "browserSources = true;" : ""} }; migrationEntrypoint = "dist/migrate.js"; - installServer = { libexecName = ${JSON.stringify(name.toLowerCase())}; descriptorPath = "descriptor.quixos-package.gen.txtpb"; }; + installServer = { libexecName = ${JSON.stringify(name.toLowerCase())}; descriptorPath = "descriptor.quixos-package.txtpb"; }; }; }\n`, ); @@ -282,7 +282,7 @@ export const scaffoldRecipe = async ( const implementation = command === "migration" ? `import type {MigrationContext} from "@quixos/camino-package-runtime";\nexport const handler = async (_context: MigrationContext): Promise => { throw new Error(${JSON.stringify(`Implement migration ${name}`)}); };\n` - : `import type {Implementation} from "../gen/qx.gen.js";\nexport const handler: Implementation[${JSON.stringify(name)}] = ${derived ? '{kind: "derived", get: ' : ""}async (_context) => { throw new Error(${JSON.stringify(`Implement ${name}`)}); }${derived ? "}" : ""};\n`; + : `import type {Implementation} from "../gen/qx.js";\nexport const handler: Implementation[${JSON.stringify(name)}] = ${derived ? '{kind: "derived", get: ' : ""}async (_context) => { throw new Error(${JSON.stringify(`Implement ${name}`)}); }${derived ? "}" : ""};\n`; create(file, implementation); packageModel.exports.push({ name, id: spec.id, file, ...(command === "migration" ? { migration: true } : {}) }); if (command === "migration") { @@ -333,7 +333,7 @@ export const scaffoldRecipe = async ( } else { create( "src/server.ts", - `import {servePackageRuntime} from "@quixos/camino-package-runtime";\nimport {createRuntime} from "./gen/qx.gen.js";\n` + + `import {servePackageRuntime} from "@quixos/camino-package-runtime";\nimport {createRuntime} from "./gen/qx.js";\n` + packageModel.exports .filter((entry) => !entry.migration) .map( @@ -364,7 +364,7 @@ export const scaffoldRecipe = async ( ); } generated( - "descriptor.quixos-package.gen.txtpb", + "descriptor.quixos-package.txtpb", `# Generated by qx-scaffold-v1\npackage_id: ${JSON.stringify(packageModel.id)}\npackage_revision_id: ${JSON.stringify(packageModel.revision)}\nruntime_protocol_version: "quixos-capabilities-v1"\n` + packageModel.exports .map( @@ -403,7 +403,7 @@ export const scaffoldRecipe = async ( } // A composed React recipe supplies its own modules and complete server. if (reactRecipe(spec)) { - for (const file of ["src/component.tsx", "src/impl/sourceGet.ts", "descriptor.quixos-package.gen.txtpb"]) + for (const file of ["src/component.tsx", "src/impl/sourceGet.ts", "descriptor.quixos-package.txtpb"]) if (!(file in spec.initialFiles)) { const index = files.findIndex((entry) => entry.file === prefix + file); if (index >= 0) files.splice(index, 1); diff --git a/src/capability-language/tool-cli.ts b/src/capability-language/tool-cli.ts index b2c8329..feae14a 100644 --- a/src/capability-language/tool-cli.ts +++ b/src/capability-language/tool-cli.ts @@ -437,7 +437,6 @@ const main = async () => { for (const [executable, args] of [ ["corepack", ["yarn", "plugin", "import", toolchain.nixifyPluginUrl]], ["corepack", ["yarn", "config", "set", "generateDefaultNix", "false"]], - ["corepack", ["yarn", "config", "set", "nixExprPath", "yarn-project.gen.nix"]], ["corepack", ["yarn", "config", "set", "individualNixPackaging", "true"]], ["corepack", ["yarn", "install"]], ] as const) { @@ -448,10 +447,10 @@ const main = async () => { ); } try { - await readFile(path.join(cwd, "yarn-project.gen.nix")); + await readFile(path.join(cwd, "yarn-project.nix")); } catch { throw new Error( - "Nixify did not generate yarn-project.gen.nix. It skips repositories under the OS temporary directory; use an ordinary workspace checkout and retry installation.", + "Nixify did not generate yarn-project.nix. It skips repositories under the OS temporary directory; use an ordinary workspace checkout and retry installation.", ); } await snapshotCommit(cwd); diff --git a/test/scaffold-recipes.test.ts b/test/scaffold-recipes.test.ts index 56d02d6..f933301 100644 --- a/test/scaffold-recipes.test.ts +++ b/test/scaffold-recipes.test.ts @@ -28,10 +28,7 @@ test("React preset applies its browser build script and shared-platform imports" await applyStructure(await planStructure(root, request)); assert.match(await fs.readFile(path.join(root, "src/impl/sourceGet.ts"), "utf8"), /component.js\?browser-source/); assert.match(await fs.readFile(path.join(root, "flake.nix"), "utf8"), /browserSources = true/); - assert.equal( - await fs.readFile(path.join(root, "src/gen/web-studio-react-runtime.gen.d.ts"), "utf8"), - reactPlatformTypes, - ); + assert.equal(await fs.readFile(path.join(root, "src/gen/web-studio-react-runtime.d.ts"), "utf8"), reactPlatformTypes); assert.match(await fs.readFile(path.join(root, "src/browser-assets.d.ts"), "utf8"), /declare module "\*\.css"/); assert.equal( JSON.parse(await fs.readFile(path.join(root, "quixos.check.json"), "utf8")).options.messages[ @@ -65,7 +62,7 @@ test("imperative scaffolds preserve authored wiring; migration sealing is explic await apply("function", { ...base, name: "play", id: "export:play" }); const filename = path.join(root, base.directory, "src/impl/play.ts"); const edited = - 'import type {Implementation} from "../gen/qx.gen.js";\nexport const handler: Implementation["play"] = async () => null;\n'; + 'import type {Implementation} from "../gen/qx.js";\nexport const handler: Implementation["play"] = async () => null;\n'; await fs.writeFile(filename, edited); const old = { version: 1 }, next = { version: 2 }, diff --git a/yarn-project.gen.nix b/yarn-project.nix similarity index 100% rename from yarn-project.gen.nix rename to yarn-project.nix From 52803dda059c6afd33c8bb66dfb69d7d644a96ab Mon Sep 17 00:00:00 2001 From: "Timothy J. Aveni" Date: Wed, 16 Sep 2026 00:12:39 -0700 Subject: [PATCH 10/11] Implement capability generics, checked package specializations and CRUD scaffolding Add kinded parameters, capability bounds, Self, aliases and closed application identities. Check generic implementations universally and build candidate-specific codecs and descriptors from immutable schemas. Preserve lexical aliases and exact dispatch identities in package and host bindings. Add an imperative CRUD+index domain scaffold with explicit soft-deletion semantics, source/codegen regression coverage, installed CLI tests and an authoring guide. Existing Web Studio opaque props and class-level create-menu migration are separate from the implemented language core. --- grammar/QuixosCapability.g4 | 62 +- nix/checked-candidate.nix | 12 +- src/bindings/client.ts | 42 +- src/bindings/generics.ts | 160 + src/bindings/index.ts | 91 +- src/capability-language/assembly.ts | 5 +- src/capability-language/candidate-check.ts | 9 +- .../generated/QuixosCapability.interp | 22 +- .../generated/QuixosCapability.tokens | 436 +-- .../generated/QuixosCapabilityLexer.interp | 23 +- .../generated/QuixosCapabilityLexer.tokens | 436 +-- .../generated/QuixosCapabilityLexer.ts | 1082 +++--- .../generated/QuixosCapabilityParser.ts | 3231 +++++++++++------ .../generated/QuixosCapabilityVisitor.ts | 42 + src/capability-language/generic-types.ts | 392 ++ src/capability-language/parser.ts | 603 ++- src/capability-language/structural-plan.ts | 2 +- src/capability-language/workspace-cli.ts | 22 +- src/capability-model/evolution.ts | 3 + src/capability-model/generic-packages.ts | 141 + src/capability-model/generics.ts | 420 +++ src/capability-model/index.ts | 2 + src/capability-model/types.ts | 44 +- src/capability-model/validation.ts | 120 + test/generic-packages.test.ts | 199 + test/generics.test.ts | 601 +++ 26 files changed, 6009 insertions(+), 2193 deletions(-) create mode 100644 src/bindings/generics.ts create mode 100644 src/capability-language/generic-types.ts create mode 100644 src/capability-model/generic-packages.ts create mode 100644 src/capability-model/generics.ts create mode 100644 test/generic-packages.test.ts create mode 100644 test/generics.test.ts diff --git a/grammar/QuixosCapability.g4 b/grammar/QuixosCapability.g4 index 2555174..b1825e4 100644 --- a/grammar/QuixosCapability.g4 +++ b/grammar/QuixosCapability.g4 @@ -27,6 +27,7 @@ workspaceItem | sharedAttachmentDecl | conformanceDecl | constructorBindingDecl + | typeAliasDecl ; resourceImportDecl @@ -46,6 +47,7 @@ resourcePreamble : resourceImportDecl | externalAtomDecl | externalInterfaceDecl + | typeAliasDecl ; atomDecl @@ -53,10 +55,39 @@ atomDecl ; interfaceResourceDecl - : resourcePreamble* INTERFACE identifier ID stringLiteral REVISION stringLiteral + : resourcePreamble* INTERFACE identifier typeParameters? ID stringLiteral REVISION stringLiteral + (REQUIRES interfaceType (COMMA interfaceType)*)? LBRACE interfaceMember* RBRACE ; +typeParameters + : LT typeParameter (COMMA typeParameter)* GT + ; + +typeParameter + : VALUE identifier (COLON STORABLE)? + | OBJECT identifier (IMPLEMENTS interfaceType (AMP interfaceType)*)? + ; + +interfaceType + : identifier typeArguments? + ; + +typeArguments + : LT typeArgument (COMMA typeArgument)* GT + ; + +typeArgument + : ATOM identifier + | INTERFACE interfaceType + | OBJECT identifier + | valueType + ; + +typeAliasDecl + : TYPE identifier typeParameters? EQUAL valueType SEMI + ; + interfaceMember : valueMember | relationshipMember @@ -93,7 +124,8 @@ relationshipOperation targetConstraint : ATOM identifier - | INTERFACE identifier + | INTERFACE identifier typeArguments? + | OBJECT identifier ; packageResourceDecl @@ -109,12 +141,12 @@ packageExport ; packageOperationExport - : OPERATION identifier ID stringLiteral COLON valueType ARROW valueType + : OPERATION identifier typeParameters? ID stringLiteral COLON valueType ARROW valueType MODE operationMode eventClause? RECEIVER receiverRequirement dependencyBlock? SEMI ; packageFunctionExport - : FUNCTION identifier ID stringLiteral COLON valueType ARROW valueType + : FUNCTION identifier typeParameters? ID stringLiteral COLON valueType ARROW valueType dependencyBlock? SEMI ; @@ -138,7 +170,8 @@ operationMode receiverRequirement : ANY | ATOM identifier - | INTERFACES LBRACK identifierList? RBRACK + | OBJECT identifier + | INTERFACES LBRACK (interfaceType (COMMA interfaceType)*)? RBRACK ; identifierList @@ -152,7 +185,7 @@ dependencyBlock dependencyPort : STATE identifier ID stringLiteral COLON valueType primitiveList SEMI | EDGE identifier ID stringLiteral COLON cardinality targetConstraint primitiveList SEMI - | INTERFACE identifier ID stringLiteral COLON identifier SEMI + | INTERFACE identifier ID stringLiteral COLON identifier typeArguments? SEMI | CONSTRUCTOR identifier ID stringLiteral COLON identifier (INPUT valueType)? SEMI ; @@ -199,7 +232,7 @@ edgeEndpoint ; conformanceDecl - : CONFORM identifier AS identifier (ID stringLiteral)? (SEMANTIC_MAJOR INTEGER)? + : CONFORM identifier AS identifier typeArguments? (ID stringLiteral)? (SEMANTIC_MAJOR INTEGER)? LBRACE conformanceItem* RBRACE ; @@ -238,7 +271,7 @@ operationName operationProvider : STATE identifier DOT statePrimitive | EDGE identifier DOT identifier DOT edgePrimitive - | PACKAGE identifier DOT identifier dependencyBindingBlock? + | PACKAGE identifier DOT identifier typeArguments? dependencyBindingBlock? ; statePrimitive @@ -263,7 +296,7 @@ dependencyBindingBlock dependencyBinding : identifier TO STATE identifier (VIA EDGE identifier DOT identifier)? SEMI | identifier TO EDGE identifier DOT identifier (VIA EDGE identifier DOT identifier)? SEMI - | identifier TO INTERFACE identifier (VIA EDGE identifier DOT identifier)? SEMI + | identifier TO INTERFACE identifier typeArguments? (VIA EDGE identifier DOT identifier)? SEMI | identifier TO CONSTRUCTOR identifier SEMI ; @@ -277,10 +310,12 @@ valueType | WATCH_HANDLE | MESSAGE stringLiteral | ATOM_REF LT identifier GT - | INTERFACE_REF LT identifier GT + | INTERFACE_REF LT identifier typeArguments? GT + | REF LT identifier GT | OPTIONAL LT valueType GT | LIST LT valueType GT | RECORD LBRACE recordField* RBRACE + | identifier typeArguments? ; recordField @@ -338,6 +373,11 @@ stringLiteral ; WORKSPACE: 'workspace'; +TYPE: 'type'; +OBJECT: 'object'; +STORABLE: 'storable'; +IMPLEMENTS: 'implements'; +REF: 'ref'; FRAGMENT: 'fragment'; IMPORT: 'import'; EXTERNAL: 'external'; @@ -441,6 +481,8 @@ LPAREN: '('; RPAREN: ')'; LT: '<'; GT: '>'; +AMP: '&'; +EQUAL: '='; INTEGER: '-'? [0-9]+; JSON_NUMBER: '-'? ('0' | [1-9] [0-9]*) ('.' [0-9]+)? ([eE] [+-]? [0-9]+)?; diff --git a/nix/checked-candidate.nix b/nix/checked-candidate.nix index d2eebd0..fabe5b9 100644 --- a/nix/checked-candidate.nix +++ b/nix/checked-candidate.nix @@ -70,7 +70,7 @@ let ${protocol}/bin/quixos-workspace-compile --root ${node.directory} \ --source-root-commit ${pkgs.lib.escapeShellArg node.commit} \ --checkout-root "$TMPDIR/checkouts" --snapshot-map ${snapshots} \ - --graph-out "$out/graph.json" > "$out/candidate.json" + --graph-out "$out/graph.json" --schemas-out "$out/package-schemas.json" > "$out/candidate.json" '' else '' @@ -95,7 +95,15 @@ let package.quixosPackages.${system}.checkedServer or (throw "Package ${node.repository} lacks checkedServer; use the supported package scaffold."); artifact = checked { - schema = "${compiled}/bindings.json"; + schema = + if kind == "workspace" then + pkgs.writeText "package-specialization-schema.json" ( + builtins.toJSON + (builtins.fromJSON (builtins.readFile "${contract}/package-schemas.json")) + .${candidate.revision.revisionId} + ) + else + "${compiled}/bindings.json"; generator = protocol; packageRevisionId = candidate.revision.revisionId; }; diff --git a/src/bindings/client.ts b/src/bindings/client.ts index af56ad3..cac5353 100644 --- a/src/bindings/client.ts +++ b/src/bindings/client.ts @@ -2,7 +2,13 @@ import type { InterfaceRevision, ValueType } from "../capability-model/types.js" /** Host clients have no package receiver, but must use the same checked * interface signatures and argument framing as generated package ports. */ -export const generateClientContracts = (interfaces: InterfaceRevision[], messages: Record) => { +export const generateClientContracts = ( + interfaces: InterfaceRevision[], + messages: Record, + qualified = false, +) => { + if (interfaces.some((entry) => entry.template)) + throw new Error("Host contracts require closed interface applications, not generic definitions"); const type = (value: ValueType): string => { switch (value.kind) { case "builtin": @@ -19,7 +25,9 @@ export const generateClientContracts = (interfaces: InterfaceRevision[], message uint64: "bigint", }[value.name]; case "object-ref": - return `{readonly $quixosRef: string}`; + return qualified + ? `CapabilityReference<${JSON.stringify(value.expectation.kind === "atom" ? `atom:${value.expectation.atomId}` : `interface:${value.expectation.interfaceRevisionId}`)}>` + : `{readonly $quixosRef: string}`; case "optional": return `(${type(value.value)} | null)`; case "list": @@ -42,6 +50,30 @@ export const generateClientContracts = (interfaces: InterfaceRevision[], message .map((operation) => ({ ...operation, interfaceRevisionId: iface.revisionId })), ), ); + if (qualified) { + const contracts = interfaces.map((iface) => { + const members = iface.members.flatMap((member) => + member.operations + .filter((op) => op.mode === "call") + .map((op) => ` ${JSON.stringify(op.id)}: {input: ${type(op.inputType)}; output: ${type(op.outputType)}};`), + ); + return `${JSON.stringify(iface.revisionId)}: {\n${members.join("\n")}\n}`; + }); + if (new Set(interfaces.map((iface) => iface.revisionId)).size !== interfaces.length) + throw new Error("Duplicate closed interface identity"); + return ( + `// Generated closed capability contracts. Dispatch by interface AND operation.\n` + + `declare const referenceType: unique symbol;\nexport type CapabilityReference = {readonly $quixosRef: string; readonly [referenceType]: T};\n` + + `export type CapabilityContracts = {${contracts.join(";\n")}};\n` + + `export type CapabilityInput = CapabilityContracts[I][O] extends {input: infer T} ? T : never;\n` + + `export type CapabilityOutput = CapabilityContracts[I][O] extends {output: infer T} ? T : never;\n` + + `export const capabilityApplications = ${JSON.stringify(Object.fromEntries(interfaces.map((iface) => [iface.revisionId, { definitionId: iface.application?.definitionId ?? iface.revisionId, arguments: iface.application?.arguments ?? [], ...(iface.application?.self ? { self: iface.application.self } : {}) }])))} as const;\n` + ); + } + if (new Set(operations.map((entry) => entry.id)).size !== operations.length) + throw new Error( + "Host operation IDs are ambiguous across interfaces; select a closed interface application explicitly", + ); return ( `// Generated from checked QX interfaces. Regenerate with scripts/generate-platform-contracts.mjs.\n` + `export type PlatformInputs = {\n${operations.map((operation) => ` ${JSON.stringify(operation.id)}: ${type(operation.inputType)};`).join("\n")}\n};\n` + @@ -65,3 +97,9 @@ export const generateClientContracts = (interfaces: InterfaceRevision[], message )} as const;\n` ); }; + +/** New consumers use exact closed interface identities; operation IDs alone are not unique. */ +export const generateAppliedClientContracts = ( + interfaces: InterfaceRevision[], + messages: Record = {}, +) => generateClientContracts(interfaces, messages, true); diff --git a/src/bindings/generics.ts b/src/bindings/generics.ts new file mode 100644 index 0000000..a6c3011 --- /dev/null +++ b/src/bindings/generics.ts @@ -0,0 +1,160 @@ +import type { GenericPackageExport, GenericDependencyPort } from "../capability-model/generic-packages.js"; +import type { InterfaceRevision, ValueType } from "../capability-model/types.js"; +import type { + ValueTypeExpression, + ObjectTypeExpression, + TypeArgumentExpression, + TypeParameter, + ValueAliasDefinition, +} from "../capability-model/generics.js"; + +/** Universal source contracts. Only closed exports get executable codecs. */ +export const genericImplementationType = ( + definition: GenericPackageExport, + interfaces: InterfaceRevision[], + concrete: (type: ValueType) => string, +): string => { + const names = new Map(definition.parameters.map((parameter, index) => [parameter.id, `T${index}`])); + type Scope = { arguments: Map; aliases: ValueAliasDefinition[] }; + const rootScope = (): Scope => ({ arguments: new Map(), aliases: definition.aliases }); + const object = (entries: [string, string][]) => + `{${entries.map(([key, value]) => `${JSON.stringify(key)}:${value}`).join(";")}}`; + const target = (type: ObjectTypeExpression, scope: Scope): string => { + if (type.kind === "parameter") { + const bound = scope.arguments.get(type.parameterId); + if (bound) return bound; + const name = names.get(type.parameterId); + if (!name) throw new Error(`Unbound object parameter ${type.parameterId}`); + return name; + } + if (type.kind === "atom") return JSON.stringify(`atom:${type.atomId}`); + if (type.kind === "interface") return JSON.stringify(`interface:${type.interfaceRevisionId}`); + if (type.kind === "application") + return `QxApplied<${JSON.stringify(type.application.definitionId)}, [${type.application.arguments.map((arg) => argument(arg, scope)).join(",")}]>`; + throw new Error("Generic package Self must be expressed as an explicit object parameter"); + }; + const argument = (arg: TypeArgumentExpression, scope: Scope): string => + arg.kind === "value" ? value(arg.type, scope) : target(arg.target, scope); + const bind = (parameters: TypeParameter[], args: TypeArgumentExpression[], scope: Scope): Scope => { + if (parameters.length !== args.length) throw new Error("Wrong generic arity during code generation"); + const result = new Map(scope.arguments); + // Render arguments in their original lexical scope before introducing binders. + const rendered = args.map((arg) => argument(arg, scope)); + parameters.forEach((parameter, index) => result.set(parameter.id, rendered[index])); + return { ...scope, arguments: result }; + }; + const value = (type: ValueTypeExpression, scope: Scope, depth = 0): string => { + if (depth > 128) throw new Error("Generic type expansion exceeds depth limit"); + switch (type.kind) { + case "parameter": { + const bound = scope.arguments.get(type.parameterId); + if (bound) return bound; + const name = names.get(type.parameterId); + if (!name) throw new Error(`Unbound value parameter ${type.parameterId}`); + return name; + } + case "object-ref": + return `QxObjectRef<${target(type.expectation, scope)}>`; + case "record": + return object(Object.entries(type.fields).map(([name, field]) => [name, value(field, scope, depth + 1)])); + case "optional": + return `(${value(type.value, scope, depth + 1)} | null)`; + case "list": + return `Array<${value(type.value, scope, depth + 1)}>`; + case "alias": { + const alias = scope.aliases.find((entry) => entry.id === type.definitionId); + if (!alias) throw new Error(`Missing alias ${type.definitionId}`); + return value(alias.body, bind(alias.parameters, type.arguments, scope), depth + 1); + } + default: + return concrete(type); + } + }; + const params = (type: ValueTypeExpression, scope: Scope) => + type.kind === "builtin" && type.name === "unit" ? "" : `input:${value(type, scope)}`; + const port = (entry: GenericDependencyPort): string => { + const requirement = entry.requirement, + scope = rootScope(); + switch (requirement.kind) { + case "state": + return object([ + ...requirement.primitives.map((primitive): [string, string] => { + if (primitive === "read") return ["get", `()=>Promise<${value(requirement.valueType, scope)}>`]; + if (primitive === "write") return ["set", `(value:${value(requirement.valueType, scope)})=>Promise`]; + throw new Error(`Unsupported generic state primitive ${primitive}`); + }), + ...(requirement.primitives.includes("read") + ? [["live", "()=>Promise"] as [string, string]] + : []), + ]); + case "edge": { + const ref = `QxObjectRef<${target(requirement.target, scope)}>`; + const methods = requirement.primitives.map((primitive): [string, string] => { + if (primitive === "resolve") return ["resolve", `()=>Promise>`]; + if (primitive === "connect" || primitive === "disconnect") + return [primitive, `(target:${ref})=>Promise`]; + throw new Error(`Unsupported generic edge primitive ${primitive}`); + }); + if (requirement.primitives.includes("resolve")) + methods.push(["collection", `()=>Promise>`]); + if ( + ["resolve", "connect", "disconnect"].every((primitive) => + requirement.primitives.includes(primitive as "resolve"), + ) + ) + methods.push([ + "replace", + `(entries:RelationshipEntry<${ref}>[],expectedRevision:bigint)=>Promise>`, + ]); + return object(methods); + } + case "constructor": + return object([ + [ + "construct", + `(${params(requirement.inputType, scope)})=>Promise>`, + ], + ]); + case "interface": { + const contract = interfaces.find((entry) => entry.revisionId === requirement.application.definitionId); + if (!contract) throw new Error(`Missing generic port contract ${requirement.application.definitionId}`); + const local = { + ...bind(contract.template?.parameters ?? [], requirement.application.arguments, scope), + aliases: contract.template?.aliases ?? [], + }; + const operations = (contract.template?.members ?? contract.members).flatMap((member) => + member.operations + .filter((op) => op.mode === "call") + .map((op) => ({ ...op, name: `${member.displayName}.${op.displayName}` })), + ); + return object([ + ["objectId", `QxObjectRef<${target({ kind: "application", application: requirement.application }, scope)}>`], + ["live", object(operations.map((op) => [op.name, `(${params(op.inputType, local)})=>Promise`]))], + ...operations.map( + (op) => + [op.name, `(${params(op.inputType, local)})=>Promise<${value(op.outputType, local)}>`] as [ + string, + string, + ], + ), + ]); + } + } + }; + const declarations = definition.parameters + .map((parameter) => `${names.get(parameter.id)}${parameter.kind === "object" ? " extends string" : ""}`) + .join(","); + const receiver = definition.receiverRequirement; + const context = object([ + ["objectId", `QxObjectRef<${receiver.kind === "target" ? target(receiver.target, rootScope()) : "string"}>`], + ["input", value(definition.inputType, rootScope())], + ["ports", object(definition.dependencyPorts.map((entry) => [entry.displayName, port(entry)]))], + ]); + const contextWithLifecycle = `${context} & QxContextLifecycle<${context} & {signal?: AbortSignal}>`; + const result = value(definition.eventType ?? definition.outputType, rootScope()); + const handler = `<${declarations}>(context:${contextWithLifecycle})=>${result}|Promise<${result}>`; + const derived = `{kind:"derived";get:${handler}}`; + return definition.eventType + ? derived + : `(${handler})${definition.kind === "operation" && definition.mode === "call" ? ` | ${derived}` : ""}`; +}; diff --git a/src/bindings/index.ts b/src/bindings/index.ts index ec9f78d..27210b3 100644 --- a/src/bindings/index.ts +++ b/src/bindings/index.ts @@ -1,21 +1,74 @@ -import type { InterfaceRevision, PackageRevision, ValueType, DependencyPort } from "../capability-model/types.js"; +import type { + InterfaceRevision, + PackageRevision, + ValueType, + DependencyPort, + WorkspaceRevision, +} from "../capability-model/types.js"; import type { CompiledCapabilityResourceRepository } from "../capability-language/assembly.js"; +import { genericImplementationType } from "./generics.js"; /** Portable generator input. New backends consume this instead of the parser or TS runtime. */ export type BindingSchema = { format: "quixos-bindings"; version: 1; interfaces: InterfaceRevision[]; + interfaceTemplates?: InterfaceRevision[]; packages: PackageRevision[]; }; -export const bindingSchema = (compiled: CompiledCapabilityResourceRepository): BindingSchema => ({ +export const bindingSchema = (compiled: Pick): BindingSchema => ({ format: "quixos-bindings", version: 1, - interfaces: compiled.resources.flatMap((node) => - node.resource.kind === "interface" ? [node.resource.revision] : [], + interfaceTemplates: compiled.resources.flatMap((node) => + node.resource.kind === "interface" && node.resource.revision.template ? [node.resource.revision] : [], ), + interfaces: [ + ...new Map( + compiled.resources + .flatMap((node) => [ + ...(node.resource.kind === "interface" ? [node.resource.revision] : []), + ...(node.resource.specializations ?? []), + ]) + .filter((entry) => !entry.template) + .map((entry) => [entry.revisionId, entry]), + ).values(), + ], packages: compiled.resources.flatMap((node) => (node.resource.kind === "package" ? [node.resource.revision] : [])), }); + +export const specializeBindingSchema = ( + base: BindingSchema, + workspace: WorkspaceRevision, + revisionId: string, +): BindingSchema => { + const pkg = workspace.packageImports.find((entry) => entry.revisionId === revisionId); + if (!pkg) throw new Error(`Missing candidate package ${revisionId}`); + const needed = new Set(); + const visit = (value: unknown): void => { + if (!value || typeof value !== "object") return; + if ("interfaceRevisionId" in value && typeof value.interfaceRevisionId === "string") + needed.add(value.interfaceRevisionId); + Object.values(value).forEach(visit); + }; + visit(pkg.exports); + const interfaces = new Map(base.interfaces.map((entry) => [entry.revisionId, entry])); + for (const id of needed) { + const iface = workspace.interfaceImports.find((entry) => entry.revisionId === id); + if (iface) { + interfaces.set(id, iface); + visit(iface.members); + } + } + return { + ...base, + interfaces: [...interfaces.values()], + packages: base.packages.map((entry) => { + const candidate = workspace.packageImports.find((value) => value.revisionId === entry.revisionId); + if (!candidate) throw new Error(`Missing dependency package ${entry.revisionId}`); + return candidate; + }), + }; +}; export type TypeScriptBindingOptions = { runtimeModule?: string; /** Each export must implement MessageBinding, providing both TS type and wire codec. */ @@ -46,6 +99,8 @@ export const generateTypeScriptBindings = ( ) => { if (schema.format !== "quixos-bindings" || schema.version !== 1) throw new Error("Unsupported binding schema version"); + if (schema.interfaces.some((entry) => entry.template)) + throw new Error("Package bindings require closed interface applications, not generic definitions"); const pkg = schema.packages.find((entry) => entry.revisionId === packageRevisionId); if (!pkg) throw new Error(`Unknown package revision ${packageRevisionId}`); const messages = new Map(); @@ -174,6 +229,7 @@ export const generateTypeScriptBindings = ( const specs: Record = {}; const contexts: [string, string][] = []; const handlers: [string, string][] = []; + const results: [string, string][] = []; for (const entry of exports) { if (names.has(entry.displayName)) throw new Error(`Duplicate export name ${entry.displayName}`); names.add(entry.displayName); @@ -200,13 +256,15 @@ export const generateTypeScriptBindings = ( const event = entry.kind === "operation" ? entry.eventType : undefined; const contextType = `Contexts[${q(entry.displayName)}]`; const outputType = type(event ?? entry.outputType); + results.push([entry.displayName, outputType]); // Watch-start handlers produce events through the runtime's derived stream protocol. - handlers.push([ - entry.displayName, - event - ? `QxDerived<${contextType}, ${outputType}>` - : `QxHandler<${contextType}, ${outputType}>${entry.kind === "operation" && entry.mode === "call" ? ` | QxDerived<${contextType}, ${outputType}>` : ""}`, - ]); + if (!entry.application) + handlers.push([ + entry.displayName, + event + ? `QxDerived<${contextType}, ${outputType}>` + : `QxHandler<${contextType}, ${outputType}>${entry.kind === "operation" && entry.mode === "call" ? ` | QxDerived<${contextType}, ${outputType}>` : ""}`, + ]); specs[entry.displayName] = { inputType: entry.inputType, outputType: entry.outputType, @@ -214,13 +272,19 @@ export const generateTypeScriptBindings = ( ports: Object.fromEntries(ports.map((port) => [port.name, port.spec])), }; } + for (const definition of pkg.genericExports ?? []) { + handlers.push([ + definition.displayName, + genericImplementationType(definition, [...schema.interfaces, ...(schema.interfaceTemplates ?? [])], type), + ]); + } const imports = [...messages].map(([id, alias]) => { const binding = options.messages![id]!; if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(binding.export)) throw new Error(`Invalid message binding export ${binding.export}`); return `import { ${binding.export} as ${alias} } from ${q(binding.module)};`; }); - const signatures = `${object(contexts)} ${object(handlers)}`; + const signatures = `${object(contexts)} ${object(handlers)} ${object(results)}`; const typeImports = [ "BindingValue", "QxObjectRef", @@ -237,14 +301,15 @@ export const generateTypeScriptBindings = ( `import { ${exports.length ? "bindQxHandler, " : ""}${[...typeImports, "QxHandlerSpec", "QxMessages"].map((name) => `type ${name}`).join(", ")} } from ${q(options.runtimeModule ?? "@quixos/camino-package-runtime")};\n` + imports.join("\n") + `\nexport const packageRevisionId = ${q(pkg.revisionId)};\n` + - `export type Contexts = ${object(contexts)};\nexport type Implementation = ${object(handlers)};\n` + + `declare const appliedType: unique symbol;\ntype QxApplied = string & {readonly [appliedType]: (value: [Definition, Arguments]) => [Definition, Arguments]};\n` + + `export type Contexts = ${object(contexts)};\nexport type Results = ${object(results)};\nexport type Implementation = ${object(handlers)};\n` + `const messages = { ${[...messages].map(([id, alias]) => `${q(id)}: ${alias}`).join(", ")} } satisfies QxMessages;\n` + `const specs = ${JSON.stringify(specs, null, 2)} satisfies Record;\n` + `export const createRuntime = (implementation: Implementation) => ({\n packageRevisionId,\n exports: {\n` + exports .map( (entry) => - ` ${q(entry.id)}: bindQxHandler(specs[${q(entry.displayName)}], implementation[${q(entry.displayName)}], messages),`, + ` ${q(entry.id)}: bindQxHandler(specs[${q(entry.displayName)}], implementation[${q(entry.application ? pkg.genericExports!.find((definition) => definition.id === entry.application!.exportId)!.displayName : entry.displayName)}], messages),`, ) .join("\n") + `\n },\n});\n` diff --git a/src/capability-language/assembly.ts b/src/capability-language/assembly.ts index 8a8fd91..ca13370 100644 --- a/src/capability-language/assembly.ts +++ b/src/capability-language/assembly.ts @@ -162,7 +162,10 @@ const environmentFor = ( ), ), interfaceClosure: exactRevisions( - closure.flatMap((node) => (node.resource.kind === "interface" ? [node.resource.revision] : [])), + closure.flatMap((node) => [ + ...(node.resource.kind === "interface" ? [node.resource.revision] : []), + ...(node.resource.specializations ?? []), + ]), ), packageClosure: exactRevisions( closure.flatMap((node) => (node.resource.kind === "package" ? [node.resource.revision] : [])), diff --git a/src/capability-language/candidate-check.ts b/src/capability-language/candidate-check.ts index 6ccb5ed..6a67de9 100644 --- a/src/capability-language/candidate-check.ts +++ b/src/capability-language/candidate-check.ts @@ -12,7 +12,7 @@ import { type EvolutionReview, type WorkspaceRevision, } from "../capability-model/index.js"; -import { bindingSchema } from "../bindings/index.js"; +import { bindingSchema, specializeBindingSchema } from "../bindings/index.js"; import { snapshotCommit, checkoutCommit, buildCheckedPackage } from "./checked-build.js"; const execFile = promisify(execFileCallback); const bytesDigest = (value: Uint8Array) => `sha256:${createHash("sha256").update(value).digest("hex")}`; @@ -219,7 +219,12 @@ export const checkWorkspaceCandidate = async (options: { resolveResource, }); const schema = path.join(temporary, "bindings.json"); - await fs.writeFile(schema, JSON.stringify(bindingSchema(candidate))); + await fs.writeFile( + schema, + JSON.stringify( + specializeBindingSchema(bindingSchema(candidate), compiled.workspace, candidate.resource.revision.revisionId), + ), + ); const artifactPath = await buildCheckedPackage( resource.directory, schema, diff --git a/src/capability-language/generated/QuixosCapability.interp b/src/capability-language/generated/QuixosCapability.interp index b9a41e7..ce06dea 100644 --- a/src/capability-language/generated/QuixosCapability.interp +++ b/src/capability-language/generated/QuixosCapability.interp @@ -1,6 +1,11 @@ token literal names: null 'workspace' +'type' +'object' +'storable' +'implements' +'ref' 'fragment' 'import' 'external' @@ -103,6 +108,8 @@ null ')' '<' '>' +'&' +'=' null null null @@ -114,6 +121,11 @@ null token symbolic names: null WORKSPACE +TYPE +OBJECT +STORABLE +IMPLEMENTS +REF FRAGMENT IMPORT EXTERNAL @@ -216,6 +228,8 @@ LPAREN RPAREN LT GT +AMP +EQUAL INTEGER JSON_NUMBER IDENTIFIER @@ -236,6 +250,12 @@ externalInterfaceDecl resourcePreamble atomDecl interfaceResourceDecl +typeParameters +typeParameter +interfaceType +typeArguments +typeArgument +typeAliasDecl interfaceMember operationMember valueMember @@ -287,4 +307,4 @@ stringLiteral atn: -[4, 1, 110, 821, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 3, 0, 131, 8, 0, 1, 1, 1, 1, 1, 1, 5, 1, 136, 8, 1, 10, 1, 12, 1, 139, 9, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 5, 3, 157, 8, 3, 10, 3, 12, 3, 160, 9, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 3, 4, 170, 8, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 182, 8, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 3, 8, 201, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 209, 8, 9, 1, 9, 1, 9, 1, 10, 5, 10, 214, 8, 10, 10, 10, 12, 10, 217, 9, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 5, 10, 227, 8, 10, 10, 10, 12, 10, 230, 9, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 3, 11, 237, 8, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 5, 13, 262, 8, 13, 10, 13, 12, 13, 265, 9, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 3, 14, 288, 8, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 3, 15, 298, 8, 15, 1, 15, 1, 15, 5, 15, 302, 8, 15, 10, 15, 12, 15, 305, 9, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 3, 16, 333, 8, 16, 1, 17, 1, 17, 1, 17, 1, 17, 3, 17, 339, 8, 17, 1, 18, 5, 18, 342, 8, 18, 10, 18, 12, 18, 345, 9, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 3, 18, 355, 8, 18, 1, 18, 1, 18, 5, 18, 359, 8, 18, 10, 18, 12, 18, 362, 9, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 3, 19, 369, 8, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 3, 20, 382, 8, 20, 1, 20, 1, 20, 1, 20, 3, 20, 387, 8, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 3, 21, 400, 8, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 3, 22, 413, 8, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 3, 25, 428, 8, 25, 1, 25, 3, 25, 431, 8, 25, 1, 26, 1, 26, 1, 26, 5, 26, 436, 8, 26, 10, 26, 12, 26, 439, 9, 26, 1, 27, 1, 27, 1, 27, 5, 27, 444, 8, 27, 10, 27, 12, 27, 447, 9, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 3, 28, 486, 8, 28, 1, 28, 1, 28, 3, 28, 490, 8, 28, 1, 29, 1, 29, 1, 29, 1, 29, 5, 29, 496, 8, 29, 10, 29, 12, 29, 499, 9, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 3, 32, 510, 8, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 3, 33, 524, 8, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 3, 34, 534, 8, 34, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 3, 36, 552, 8, 36, 1, 36, 1, 36, 3, 36, 556, 8, 36, 1, 36, 3, 36, 559, 8, 36, 1, 36, 1, 36, 3, 36, 563, 8, 36, 1, 36, 3, 36, 566, 8, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 3, 37, 576, 8, 37, 1, 37, 1, 37, 3, 37, 580, 8, 37, 1, 37, 1, 37, 5, 37, 584, 8, 37, 10, 37, 12, 37, 587, 9, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 3, 38, 595, 8, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 3, 42, 632, 8, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 3, 43, 651, 8, 43, 3, 43, 653, 8, 43, 1, 44, 1, 44, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 5, 46, 662, 8, 46, 10, 46, 12, 46, 665, 9, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 3, 47, 679, 8, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 3, 47, 695, 8, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 3, 47, 709, 8, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 3, 47, 719, 8, 47, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 3, 48, 728, 8, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 5, 49, 760, 8, 49, 10, 49, 12, 49, 763, 9, 49, 1, 49, 3, 49, 766, 8, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 3, 53, 785, 8, 53, 1, 54, 1, 54, 1, 54, 1, 54, 5, 54, 791, 8, 54, 10, 54, 12, 54, 794, 9, 54, 3, 54, 796, 8, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 56, 5, 56, 808, 8, 56, 10, 56, 12, 56, 811, 9, 56, 3, 56, 813, 8, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 0, 0, 59, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 0, 7, 1, 0, 60, 64, 2, 0, 55, 59, 61, 62, 2, 0, 55, 56, 61, 62, 2, 0, 57, 59, 61, 62, 1, 0, 80, 87, 1, 0, 67, 70, 2, 0, 34, 34, 106, 106, 861, 0, 130, 1, 0, 0, 0, 2, 132, 1, 0, 0, 0, 4, 142, 1, 0, 0, 0, 6, 146, 1, 0, 0, 0, 8, 169, 1, 0, 0, 0, 10, 181, 1, 0, 0, 0, 12, 183, 1, 0, 0, 0, 14, 190, 1, 0, 0, 0, 16, 200, 1, 0, 0, 0, 18, 202, 1, 0, 0, 0, 20, 215, 1, 0, 0, 0, 22, 236, 1, 0, 0, 0, 24, 238, 1, 0, 0, 0, 26, 253, 1, 0, 0, 0, 28, 287, 1, 0, 0, 0, 30, 289, 1, 0, 0, 0, 32, 332, 1, 0, 0, 0, 34, 338, 1, 0, 0, 0, 36, 343, 1, 0, 0, 0, 38, 368, 1, 0, 0, 0, 40, 370, 1, 0, 0, 0, 42, 390, 1, 0, 0, 0, 44, 403, 1, 0, 0, 0, 46, 416, 1, 0, 0, 0, 48, 419, 1, 0, 0, 0, 50, 430, 1, 0, 0, 0, 52, 432, 1, 0, 0, 0, 54, 440, 1, 0, 0, 0, 56, 489, 1, 0, 0, 0, 58, 491, 1, 0, 0, 0, 60, 502, 1, 0, 0, 0, 62, 504, 1, 0, 0, 0, 64, 509, 1, 0, 0, 0, 66, 511, 1, 0, 0, 0, 68, 533, 1, 0, 0, 0, 70, 535, 1, 0, 0, 0, 72, 544, 1, 0, 0, 0, 74, 569, 1, 0, 0, 0, 76, 594, 1, 0, 0, 0, 78, 596, 1, 0, 0, 0, 80, 610, 1, 0, 0, 0, 82, 616, 1, 0, 0, 0, 84, 631, 1, 0, 0, 0, 86, 652, 1, 0, 0, 0, 88, 654, 1, 0, 0, 0, 90, 656, 1, 0, 0, 0, 92, 658, 1, 0, 0, 0, 94, 718, 1, 0, 0, 0, 96, 720, 1, 0, 0, 0, 98, 765, 1, 0, 0, 0, 100, 767, 1, 0, 0, 0, 102, 772, 1, 0, 0, 0, 104, 774, 1, 0, 0, 0, 106, 784, 1, 0, 0, 0, 108, 786, 1, 0, 0, 0, 110, 799, 1, 0, 0, 0, 112, 803, 1, 0, 0, 0, 114, 816, 1, 0, 0, 0, 116, 818, 1, 0, 0, 0, 118, 119, 3, 6, 3, 0, 119, 120, 5, 0, 0, 1, 120, 131, 1, 0, 0, 0, 121, 122, 3, 20, 10, 0, 122, 123, 5, 0, 0, 1, 123, 131, 1, 0, 0, 0, 124, 125, 3, 36, 18, 0, 125, 126, 5, 0, 0, 1, 126, 131, 1, 0, 0, 0, 127, 128, 3, 2, 1, 0, 128, 129, 5, 0, 0, 1, 129, 131, 1, 0, 0, 0, 130, 118, 1, 0, 0, 0, 130, 121, 1, 0, 0, 0, 130, 124, 1, 0, 0, 0, 130, 127, 1, 0, 0, 0, 131, 1, 1, 0, 0, 0, 132, 133, 5, 2, 0, 0, 133, 137, 5, 96, 0, 0, 134, 136, 3, 8, 4, 0, 135, 134, 1, 0, 0, 0, 136, 139, 1, 0, 0, 0, 137, 135, 1, 0, 0, 0, 137, 138, 1, 0, 0, 0, 138, 140, 1, 0, 0, 0, 139, 137, 1, 0, 0, 0, 140, 141, 5, 97, 0, 0, 141, 3, 1, 0, 0, 0, 142, 143, 5, 3, 0, 0, 143, 144, 3, 116, 58, 0, 144, 145, 5, 93, 0, 0, 145, 5, 1, 0, 0, 0, 146, 147, 5, 1, 0, 0, 147, 148, 3, 114, 57, 0, 148, 149, 5, 43, 0, 0, 149, 150, 3, 116, 58, 0, 150, 151, 5, 37, 0, 0, 151, 152, 3, 116, 58, 0, 152, 153, 5, 36, 0, 0, 153, 154, 3, 116, 58, 0, 154, 158, 5, 96, 0, 0, 155, 157, 3, 8, 4, 0, 156, 155, 1, 0, 0, 0, 157, 160, 1, 0, 0, 0, 158, 156, 1, 0, 0, 0, 158, 159, 1, 0, 0, 0, 159, 161, 1, 0, 0, 0, 160, 158, 1, 0, 0, 0, 161, 162, 5, 97, 0, 0, 162, 7, 1, 0, 0, 0, 163, 170, 3, 4, 2, 0, 164, 170, 3, 18, 9, 0, 165, 170, 3, 10, 5, 0, 166, 170, 3, 62, 31, 0, 167, 170, 3, 74, 37, 0, 168, 170, 3, 96, 48, 0, 169, 163, 1, 0, 0, 0, 169, 164, 1, 0, 0, 0, 169, 165, 1, 0, 0, 0, 169, 166, 1, 0, 0, 0, 169, 167, 1, 0, 0, 0, 169, 168, 1, 0, 0, 0, 170, 9, 1, 0, 0, 0, 171, 172, 5, 3, 0, 0, 172, 173, 5, 6, 0, 0, 173, 174, 3, 114, 57, 0, 174, 175, 5, 93, 0, 0, 175, 182, 1, 0, 0, 0, 176, 177, 5, 3, 0, 0, 177, 178, 5, 8, 0, 0, 178, 179, 3, 114, 57, 0, 179, 180, 5, 93, 0, 0, 180, 182, 1, 0, 0, 0, 181, 171, 1, 0, 0, 0, 181, 176, 1, 0, 0, 0, 182, 11, 1, 0, 0, 0, 183, 184, 5, 4, 0, 0, 184, 185, 5, 5, 0, 0, 185, 186, 3, 114, 57, 0, 186, 187, 5, 43, 0, 0, 187, 188, 3, 116, 58, 0, 188, 189, 5, 93, 0, 0, 189, 13, 1, 0, 0, 0, 190, 191, 5, 4, 0, 0, 191, 192, 5, 6, 0, 0, 192, 193, 3, 114, 57, 0, 193, 194, 5, 37, 0, 0, 194, 195, 3, 116, 58, 0, 195, 196, 5, 93, 0, 0, 196, 15, 1, 0, 0, 0, 197, 201, 3, 10, 5, 0, 198, 201, 3, 12, 6, 0, 199, 201, 3, 14, 7, 0, 200, 197, 1, 0, 0, 0, 200, 198, 1, 0, 0, 0, 200, 199, 1, 0, 0, 0, 201, 17, 1, 0, 0, 0, 202, 203, 5, 5, 0, 0, 203, 204, 3, 114, 57, 0, 204, 205, 5, 43, 0, 0, 205, 208, 3, 116, 58, 0, 206, 207, 5, 44, 0, 0, 207, 209, 3, 116, 58, 0, 208, 206, 1, 0, 0, 0, 208, 209, 1, 0, 0, 0, 209, 210, 1, 0, 0, 0, 210, 211, 5, 93, 0, 0, 211, 19, 1, 0, 0, 0, 212, 214, 3, 16, 8, 0, 213, 212, 1, 0, 0, 0, 214, 217, 1, 0, 0, 0, 215, 213, 1, 0, 0, 0, 215, 216, 1, 0, 0, 0, 216, 218, 1, 0, 0, 0, 217, 215, 1, 0, 0, 0, 218, 219, 5, 6, 0, 0, 219, 220, 3, 114, 57, 0, 220, 221, 5, 43, 0, 0, 221, 222, 3, 116, 58, 0, 222, 223, 5, 37, 0, 0, 223, 224, 3, 116, 58, 0, 224, 228, 5, 96, 0, 0, 225, 227, 3, 22, 11, 0, 226, 225, 1, 0, 0, 0, 227, 230, 1, 0, 0, 0, 228, 226, 1, 0, 0, 0, 228, 229, 1, 0, 0, 0, 229, 231, 1, 0, 0, 0, 230, 228, 1, 0, 0, 0, 231, 232, 5, 97, 0, 0, 232, 21, 1, 0, 0, 0, 233, 237, 3, 26, 13, 0, 234, 237, 3, 30, 15, 0, 235, 237, 3, 24, 12, 0, 236, 233, 1, 0, 0, 0, 236, 234, 1, 0, 0, 0, 236, 235, 1, 0, 0, 0, 237, 23, 1, 0, 0, 0, 238, 239, 5, 11, 0, 0, 239, 240, 3, 114, 57, 0, 240, 241, 5, 43, 0, 0, 241, 242, 3, 116, 58, 0, 242, 243, 5, 92, 0, 0, 243, 244, 3, 98, 49, 0, 244, 245, 5, 91, 0, 0, 245, 246, 3, 98, 49, 0, 246, 247, 5, 96, 0, 0, 247, 248, 5, 60, 0, 0, 248, 249, 5, 43, 0, 0, 249, 250, 3, 116, 58, 0, 250, 251, 5, 93, 0, 0, 251, 252, 5, 97, 0, 0, 252, 25, 1, 0, 0, 0, 253, 254, 5, 9, 0, 0, 254, 255, 3, 114, 57, 0, 255, 256, 5, 43, 0, 0, 256, 257, 3, 116, 58, 0, 257, 258, 5, 92, 0, 0, 258, 259, 3, 98, 49, 0, 259, 263, 5, 96, 0, 0, 260, 262, 3, 28, 14, 0, 261, 260, 1, 0, 0, 0, 262, 265, 1, 0, 0, 0, 263, 261, 1, 0, 0, 0, 263, 264, 1, 0, 0, 0, 264, 266, 1, 0, 0, 0, 265, 263, 1, 0, 0, 0, 266, 267, 5, 97, 0, 0, 267, 27, 1, 0, 0, 0, 268, 269, 5, 50, 0, 0, 269, 270, 5, 43, 0, 0, 270, 271, 3, 116, 58, 0, 271, 272, 5, 93, 0, 0, 272, 288, 1, 0, 0, 0, 273, 274, 5, 51, 0, 0, 274, 275, 5, 43, 0, 0, 275, 276, 3, 116, 58, 0, 276, 277, 5, 93, 0, 0, 277, 288, 1, 0, 0, 0, 278, 279, 5, 52, 0, 0, 279, 280, 5, 53, 0, 0, 280, 281, 5, 43, 0, 0, 281, 282, 3, 116, 58, 0, 282, 283, 5, 54, 0, 0, 283, 284, 5, 43, 0, 0, 284, 285, 3, 116, 58, 0, 285, 286, 5, 93, 0, 0, 286, 288, 1, 0, 0, 0, 287, 268, 1, 0, 0, 0, 287, 273, 1, 0, 0, 0, 287, 278, 1, 0, 0, 0, 288, 29, 1, 0, 0, 0, 289, 290, 5, 10, 0, 0, 290, 291, 3, 114, 57, 0, 291, 292, 5, 43, 0, 0, 292, 293, 3, 116, 58, 0, 293, 294, 5, 92, 0, 0, 294, 295, 3, 104, 52, 0, 295, 297, 3, 34, 17, 0, 296, 298, 5, 71, 0, 0, 297, 296, 1, 0, 0, 0, 297, 298, 1, 0, 0, 0, 298, 299, 1, 0, 0, 0, 299, 303, 5, 96, 0, 0, 300, 302, 3, 32, 16, 0, 301, 300, 1, 0, 0, 0, 302, 305, 1, 0, 0, 0, 303, 301, 1, 0, 0, 0, 303, 304, 1, 0, 0, 0, 304, 306, 1, 0, 0, 0, 305, 303, 1, 0, 0, 0, 306, 307, 5, 97, 0, 0, 307, 31, 1, 0, 0, 0, 308, 309, 5, 57, 0, 0, 309, 310, 5, 43, 0, 0, 310, 311, 3, 116, 58, 0, 311, 312, 5, 93, 0, 0, 312, 333, 1, 0, 0, 0, 313, 314, 5, 58, 0, 0, 314, 315, 5, 43, 0, 0, 315, 316, 3, 116, 58, 0, 316, 317, 5, 93, 0, 0, 317, 333, 1, 0, 0, 0, 318, 319, 5, 59, 0, 0, 319, 320, 5, 43, 0, 0, 320, 321, 3, 116, 58, 0, 321, 322, 5, 93, 0, 0, 322, 333, 1, 0, 0, 0, 323, 324, 5, 52, 0, 0, 324, 325, 5, 53, 0, 0, 325, 326, 5, 43, 0, 0, 326, 327, 3, 116, 58, 0, 327, 328, 5, 54, 0, 0, 328, 329, 5, 43, 0, 0, 329, 330, 3, 116, 58, 0, 330, 331, 5, 93, 0, 0, 331, 333, 1, 0, 0, 0, 332, 308, 1, 0, 0, 0, 332, 313, 1, 0, 0, 0, 332, 318, 1, 0, 0, 0, 332, 323, 1, 0, 0, 0, 333, 33, 1, 0, 0, 0, 334, 335, 5, 5, 0, 0, 335, 339, 3, 114, 57, 0, 336, 337, 5, 6, 0, 0, 337, 339, 3, 114, 57, 0, 338, 334, 1, 0, 0, 0, 338, 336, 1, 0, 0, 0, 339, 35, 1, 0, 0, 0, 340, 342, 3, 16, 8, 0, 341, 340, 1, 0, 0, 0, 342, 345, 1, 0, 0, 0, 343, 341, 1, 0, 0, 0, 343, 344, 1, 0, 0, 0, 344, 346, 1, 0, 0, 0, 345, 343, 1, 0, 0, 0, 346, 347, 5, 8, 0, 0, 347, 348, 3, 114, 57, 0, 348, 349, 5, 43, 0, 0, 349, 350, 3, 116, 58, 0, 350, 351, 5, 37, 0, 0, 351, 354, 3, 116, 58, 0, 352, 353, 5, 38, 0, 0, 353, 355, 5, 104, 0, 0, 354, 352, 1, 0, 0, 0, 354, 355, 1, 0, 0, 0, 355, 356, 1, 0, 0, 0, 356, 360, 5, 96, 0, 0, 357, 359, 3, 38, 19, 0, 358, 357, 1, 0, 0, 0, 359, 362, 1, 0, 0, 0, 360, 358, 1, 0, 0, 0, 360, 361, 1, 0, 0, 0, 361, 363, 1, 0, 0, 0, 362, 360, 1, 0, 0, 0, 363, 364, 5, 97, 0, 0, 364, 37, 1, 0, 0, 0, 365, 369, 3, 40, 20, 0, 366, 369, 3, 42, 21, 0, 367, 369, 3, 44, 22, 0, 368, 365, 1, 0, 0, 0, 368, 366, 1, 0, 0, 0, 368, 367, 1, 0, 0, 0, 369, 39, 1, 0, 0, 0, 370, 371, 5, 11, 0, 0, 371, 372, 3, 114, 57, 0, 372, 373, 5, 43, 0, 0, 373, 374, 3, 116, 58, 0, 374, 375, 5, 92, 0, 0, 375, 376, 3, 98, 49, 0, 376, 377, 5, 91, 0, 0, 377, 378, 3, 98, 49, 0, 378, 379, 5, 45, 0, 0, 379, 381, 3, 48, 24, 0, 380, 382, 3, 46, 23, 0, 381, 380, 1, 0, 0, 0, 381, 382, 1, 0, 0, 0, 382, 383, 1, 0, 0, 0, 383, 384, 5, 47, 0, 0, 384, 386, 3, 50, 25, 0, 385, 387, 3, 54, 27, 0, 386, 385, 1, 0, 0, 0, 386, 387, 1, 0, 0, 0, 387, 388, 1, 0, 0, 0, 388, 389, 5, 93, 0, 0, 389, 41, 1, 0, 0, 0, 390, 391, 5, 12, 0, 0, 391, 392, 3, 114, 57, 0, 392, 393, 5, 43, 0, 0, 393, 394, 3, 116, 58, 0, 394, 395, 5, 92, 0, 0, 395, 396, 3, 98, 49, 0, 396, 397, 5, 91, 0, 0, 397, 399, 3, 98, 49, 0, 398, 400, 3, 54, 27, 0, 399, 398, 1, 0, 0, 0, 399, 400, 1, 0, 0, 0, 400, 401, 1, 0, 0, 0, 401, 402, 5, 93, 0, 0, 402, 43, 1, 0, 0, 0, 403, 404, 5, 13, 0, 0, 404, 405, 3, 114, 57, 0, 405, 406, 5, 43, 0, 0, 406, 407, 3, 116, 58, 0, 407, 408, 5, 14, 0, 0, 408, 409, 3, 114, 57, 0, 409, 410, 5, 92, 0, 0, 410, 412, 3, 98, 49, 0, 411, 413, 3, 54, 27, 0, 412, 411, 1, 0, 0, 0, 412, 413, 1, 0, 0, 0, 413, 414, 1, 0, 0, 0, 414, 415, 5, 93, 0, 0, 415, 45, 1, 0, 0, 0, 416, 417, 5, 46, 0, 0, 417, 418, 3, 98, 49, 0, 418, 47, 1, 0, 0, 0, 419, 420, 7, 0, 0, 0, 420, 49, 1, 0, 0, 0, 421, 431, 5, 49, 0, 0, 422, 423, 5, 5, 0, 0, 423, 431, 3, 114, 57, 0, 424, 425, 5, 7, 0, 0, 425, 427, 5, 98, 0, 0, 426, 428, 3, 52, 26, 0, 427, 426, 1, 0, 0, 0, 427, 428, 1, 0, 0, 0, 428, 429, 1, 0, 0, 0, 429, 431, 5, 99, 0, 0, 430, 421, 1, 0, 0, 0, 430, 422, 1, 0, 0, 0, 430, 424, 1, 0, 0, 0, 431, 51, 1, 0, 0, 0, 432, 437, 3, 114, 57, 0, 433, 434, 5, 94, 0, 0, 434, 436, 3, 114, 57, 0, 435, 433, 1, 0, 0, 0, 436, 439, 1, 0, 0, 0, 437, 435, 1, 0, 0, 0, 437, 438, 1, 0, 0, 0, 438, 53, 1, 0, 0, 0, 439, 437, 1, 0, 0, 0, 440, 441, 5, 48, 0, 0, 441, 445, 5, 96, 0, 0, 442, 444, 3, 56, 28, 0, 443, 442, 1, 0, 0, 0, 444, 447, 1, 0, 0, 0, 445, 443, 1, 0, 0, 0, 445, 446, 1, 0, 0, 0, 446, 448, 1, 0, 0, 0, 447, 445, 1, 0, 0, 0, 448, 449, 5, 97, 0, 0, 449, 55, 1, 0, 0, 0, 450, 451, 5, 22, 0, 0, 451, 452, 3, 114, 57, 0, 452, 453, 5, 43, 0, 0, 453, 454, 3, 116, 58, 0, 454, 455, 5, 92, 0, 0, 455, 456, 3, 98, 49, 0, 456, 457, 3, 58, 29, 0, 457, 458, 5, 93, 0, 0, 458, 490, 1, 0, 0, 0, 459, 460, 5, 23, 0, 0, 460, 461, 3, 114, 57, 0, 461, 462, 5, 43, 0, 0, 462, 463, 3, 116, 58, 0, 463, 464, 5, 92, 0, 0, 464, 465, 3, 104, 52, 0, 465, 466, 3, 34, 17, 0, 466, 467, 3, 58, 29, 0, 467, 468, 5, 93, 0, 0, 468, 490, 1, 0, 0, 0, 469, 470, 5, 6, 0, 0, 470, 471, 3, 114, 57, 0, 471, 472, 5, 43, 0, 0, 472, 473, 3, 116, 58, 0, 473, 474, 5, 92, 0, 0, 474, 475, 3, 114, 57, 0, 475, 476, 5, 93, 0, 0, 476, 490, 1, 0, 0, 0, 477, 478, 5, 13, 0, 0, 478, 479, 3, 114, 57, 0, 479, 480, 5, 43, 0, 0, 480, 481, 3, 116, 58, 0, 481, 482, 5, 92, 0, 0, 482, 485, 3, 114, 57, 0, 483, 484, 5, 15, 0, 0, 484, 486, 3, 98, 49, 0, 485, 483, 1, 0, 0, 0, 485, 486, 1, 0, 0, 0, 486, 487, 1, 0, 0, 0, 487, 488, 5, 93, 0, 0, 488, 490, 1, 0, 0, 0, 489, 450, 1, 0, 0, 0, 489, 459, 1, 0, 0, 0, 489, 469, 1, 0, 0, 0, 489, 477, 1, 0, 0, 0, 490, 57, 1, 0, 0, 0, 491, 492, 5, 98, 0, 0, 492, 497, 3, 60, 30, 0, 493, 494, 5, 94, 0, 0, 494, 496, 3, 60, 30, 0, 495, 493, 1, 0, 0, 0, 496, 499, 1, 0, 0, 0, 497, 495, 1, 0, 0, 0, 497, 498, 1, 0, 0, 0, 498, 500, 1, 0, 0, 0, 499, 497, 1, 0, 0, 0, 500, 501, 5, 99, 0, 0, 501, 59, 1, 0, 0, 0, 502, 503, 7, 1, 0, 0, 503, 61, 1, 0, 0, 0, 504, 505, 5, 21, 0, 0, 505, 506, 3, 64, 32, 0, 506, 63, 1, 0, 0, 0, 507, 510, 3, 66, 33, 0, 508, 510, 3, 70, 35, 0, 509, 507, 1, 0, 0, 0, 509, 508, 1, 0, 0, 0, 510, 65, 1, 0, 0, 0, 511, 512, 5, 22, 0, 0, 512, 513, 3, 114, 57, 0, 513, 514, 5, 43, 0, 0, 514, 515, 3, 116, 58, 0, 515, 516, 5, 31, 0, 0, 516, 517, 3, 114, 57, 0, 517, 518, 5, 92, 0, 0, 518, 519, 3, 98, 49, 0, 519, 520, 5, 32, 0, 0, 520, 523, 3, 68, 34, 0, 521, 522, 5, 33, 0, 0, 522, 524, 3, 106, 53, 0, 523, 521, 1, 0, 0, 0, 523, 524, 1, 0, 0, 0, 524, 525, 1, 0, 0, 0, 525, 526, 5, 93, 0, 0, 526, 67, 1, 0, 0, 0, 527, 534, 5, 65, 0, 0, 528, 529, 5, 66, 0, 0, 529, 530, 5, 100, 0, 0, 530, 531, 3, 98, 49, 0, 531, 532, 5, 101, 0, 0, 532, 534, 1, 0, 0, 0, 533, 527, 1, 0, 0, 0, 533, 528, 1, 0, 0, 0, 534, 69, 1, 0, 0, 0, 535, 536, 5, 23, 0, 0, 536, 537, 3, 114, 57, 0, 537, 538, 5, 43, 0, 0, 538, 539, 3, 116, 58, 0, 539, 540, 5, 96, 0, 0, 540, 541, 3, 72, 36, 0, 541, 542, 3, 72, 36, 0, 542, 543, 5, 97, 0, 0, 543, 71, 1, 0, 0, 0, 544, 545, 3, 34, 17, 0, 545, 546, 5, 24, 0, 0, 546, 547, 3, 114, 57, 0, 547, 548, 5, 43, 0, 0, 548, 549, 3, 116, 58, 0, 549, 551, 3, 104, 52, 0, 550, 552, 5, 71, 0, 0, 551, 550, 1, 0, 0, 0, 551, 552, 1, 0, 0, 0, 552, 555, 1, 0, 0, 0, 553, 554, 5, 39, 0, 0, 554, 556, 3, 116, 58, 0, 555, 553, 1, 0, 0, 0, 555, 556, 1, 0, 0, 0, 556, 558, 1, 0, 0, 0, 557, 559, 5, 40, 0, 0, 558, 557, 1, 0, 0, 0, 558, 559, 1, 0, 0, 0, 559, 562, 1, 0, 0, 0, 560, 561, 5, 41, 0, 0, 561, 563, 3, 116, 58, 0, 562, 560, 1, 0, 0, 0, 562, 563, 1, 0, 0, 0, 563, 565, 1, 0, 0, 0, 564, 566, 5, 42, 0, 0, 565, 564, 1, 0, 0, 0, 565, 566, 1, 0, 0, 0, 566, 567, 1, 0, 0, 0, 567, 568, 5, 93, 0, 0, 568, 73, 1, 0, 0, 0, 569, 570, 5, 16, 0, 0, 570, 571, 3, 114, 57, 0, 571, 572, 5, 17, 0, 0, 572, 575, 3, 114, 57, 0, 573, 574, 5, 43, 0, 0, 574, 576, 3, 116, 58, 0, 575, 573, 1, 0, 0, 0, 575, 576, 1, 0, 0, 0, 576, 579, 1, 0, 0, 0, 577, 578, 5, 38, 0, 0, 578, 580, 5, 104, 0, 0, 579, 577, 1, 0, 0, 0, 579, 580, 1, 0, 0, 0, 580, 581, 1, 0, 0, 0, 581, 585, 5, 96, 0, 0, 582, 584, 3, 76, 38, 0, 583, 582, 1, 0, 0, 0, 584, 587, 1, 0, 0, 0, 585, 583, 1, 0, 0, 0, 585, 586, 1, 0, 0, 0, 586, 588, 1, 0, 0, 0, 587, 585, 1, 0, 0, 0, 588, 589, 5, 97, 0, 0, 589, 75, 1, 0, 0, 0, 590, 591, 5, 20, 0, 0, 591, 595, 3, 64, 32, 0, 592, 595, 3, 80, 40, 0, 593, 595, 3, 78, 39, 0, 594, 590, 1, 0, 0, 0, 594, 592, 1, 0, 0, 0, 594, 593, 1, 0, 0, 0, 595, 77, 1, 0, 0, 0, 596, 597, 5, 28, 0, 0, 597, 598, 3, 114, 57, 0, 598, 599, 5, 29, 0, 0, 599, 600, 5, 30, 0, 0, 600, 601, 5, 26, 0, 0, 601, 602, 5, 13, 0, 0, 602, 603, 3, 114, 57, 0, 603, 604, 5, 27, 0, 0, 604, 605, 5, 23, 0, 0, 605, 606, 3, 114, 57, 0, 606, 607, 5, 95, 0, 0, 607, 608, 3, 114, 57, 0, 608, 609, 5, 93, 0, 0, 609, 79, 1, 0, 0, 0, 610, 611, 5, 18, 0, 0, 611, 612, 3, 82, 41, 0, 612, 613, 5, 19, 0, 0, 613, 614, 3, 86, 43, 0, 614, 615, 5, 93, 0, 0, 615, 81, 1, 0, 0, 0, 616, 617, 3, 114, 57, 0, 617, 618, 5, 95, 0, 0, 618, 619, 3, 84, 42, 0, 619, 83, 1, 0, 0, 0, 620, 632, 3, 114, 57, 0, 621, 632, 5, 60, 0, 0, 622, 632, 5, 50, 0, 0, 623, 632, 5, 51, 0, 0, 624, 632, 5, 57, 0, 0, 625, 632, 5, 58, 0, 0, 626, 632, 5, 59, 0, 0, 627, 632, 5, 61, 0, 0, 628, 632, 5, 62, 0, 0, 629, 632, 5, 63, 0, 0, 630, 632, 5, 64, 0, 0, 631, 620, 1, 0, 0, 0, 631, 621, 1, 0, 0, 0, 631, 622, 1, 0, 0, 0, 631, 623, 1, 0, 0, 0, 631, 624, 1, 0, 0, 0, 631, 625, 1, 0, 0, 0, 631, 626, 1, 0, 0, 0, 631, 627, 1, 0, 0, 0, 631, 628, 1, 0, 0, 0, 631, 629, 1, 0, 0, 0, 631, 630, 1, 0, 0, 0, 632, 85, 1, 0, 0, 0, 633, 634, 5, 22, 0, 0, 634, 635, 3, 114, 57, 0, 635, 636, 5, 95, 0, 0, 636, 637, 3, 88, 44, 0, 637, 653, 1, 0, 0, 0, 638, 639, 5, 23, 0, 0, 639, 640, 3, 114, 57, 0, 640, 641, 5, 95, 0, 0, 641, 642, 3, 114, 57, 0, 642, 643, 5, 95, 0, 0, 643, 644, 3, 90, 45, 0, 644, 653, 1, 0, 0, 0, 645, 646, 5, 8, 0, 0, 646, 647, 3, 114, 57, 0, 647, 648, 5, 95, 0, 0, 648, 650, 3, 114, 57, 0, 649, 651, 3, 92, 46, 0, 650, 649, 1, 0, 0, 0, 650, 651, 1, 0, 0, 0, 651, 653, 1, 0, 0, 0, 652, 633, 1, 0, 0, 0, 652, 638, 1, 0, 0, 0, 652, 645, 1, 0, 0, 0, 653, 87, 1, 0, 0, 0, 654, 655, 7, 2, 0, 0, 655, 89, 1, 0, 0, 0, 656, 657, 7, 3, 0, 0, 657, 91, 1, 0, 0, 0, 658, 659, 5, 25, 0, 0, 659, 663, 5, 96, 0, 0, 660, 662, 3, 94, 47, 0, 661, 660, 1, 0, 0, 0, 662, 665, 1, 0, 0, 0, 663, 661, 1, 0, 0, 0, 663, 664, 1, 0, 0, 0, 664, 666, 1, 0, 0, 0, 665, 663, 1, 0, 0, 0, 666, 667, 5, 97, 0, 0, 667, 93, 1, 0, 0, 0, 668, 669, 3, 114, 57, 0, 669, 670, 5, 19, 0, 0, 670, 671, 5, 22, 0, 0, 671, 678, 3, 114, 57, 0, 672, 673, 5, 27, 0, 0, 673, 674, 5, 23, 0, 0, 674, 675, 3, 114, 57, 0, 675, 676, 5, 95, 0, 0, 676, 677, 3, 114, 57, 0, 677, 679, 1, 0, 0, 0, 678, 672, 1, 0, 0, 0, 678, 679, 1, 0, 0, 0, 679, 680, 1, 0, 0, 0, 680, 681, 5, 93, 0, 0, 681, 719, 1, 0, 0, 0, 682, 683, 3, 114, 57, 0, 683, 684, 5, 19, 0, 0, 684, 685, 5, 23, 0, 0, 685, 686, 3, 114, 57, 0, 686, 687, 5, 95, 0, 0, 687, 694, 3, 114, 57, 0, 688, 689, 5, 27, 0, 0, 689, 690, 5, 23, 0, 0, 690, 691, 3, 114, 57, 0, 691, 692, 5, 95, 0, 0, 692, 693, 3, 114, 57, 0, 693, 695, 1, 0, 0, 0, 694, 688, 1, 0, 0, 0, 694, 695, 1, 0, 0, 0, 695, 696, 1, 0, 0, 0, 696, 697, 5, 93, 0, 0, 697, 719, 1, 0, 0, 0, 698, 699, 3, 114, 57, 0, 699, 700, 5, 19, 0, 0, 700, 701, 5, 6, 0, 0, 701, 708, 3, 114, 57, 0, 702, 703, 5, 27, 0, 0, 703, 704, 5, 23, 0, 0, 704, 705, 3, 114, 57, 0, 705, 706, 5, 95, 0, 0, 706, 707, 3, 114, 57, 0, 707, 709, 1, 0, 0, 0, 708, 702, 1, 0, 0, 0, 708, 709, 1, 0, 0, 0, 709, 710, 1, 0, 0, 0, 710, 711, 5, 93, 0, 0, 711, 719, 1, 0, 0, 0, 712, 713, 3, 114, 57, 0, 713, 714, 5, 19, 0, 0, 714, 715, 5, 13, 0, 0, 715, 716, 3, 114, 57, 0, 716, 717, 5, 93, 0, 0, 717, 719, 1, 0, 0, 0, 718, 668, 1, 0, 0, 0, 718, 682, 1, 0, 0, 0, 718, 698, 1, 0, 0, 0, 718, 712, 1, 0, 0, 0, 719, 95, 1, 0, 0, 0, 720, 721, 5, 13, 0, 0, 721, 722, 3, 114, 57, 0, 722, 723, 5, 19, 0, 0, 723, 724, 3, 114, 57, 0, 724, 725, 5, 95, 0, 0, 725, 727, 3, 114, 57, 0, 726, 728, 3, 92, 46, 0, 727, 726, 1, 0, 0, 0, 727, 728, 1, 0, 0, 0, 728, 729, 1, 0, 0, 0, 729, 730, 5, 93, 0, 0, 730, 97, 1, 0, 0, 0, 731, 766, 3, 102, 51, 0, 732, 766, 5, 72, 0, 0, 733, 766, 5, 73, 0, 0, 734, 735, 5, 74, 0, 0, 735, 766, 3, 116, 58, 0, 736, 737, 5, 75, 0, 0, 737, 738, 5, 102, 0, 0, 738, 739, 3, 114, 57, 0, 739, 740, 5, 103, 0, 0, 740, 766, 1, 0, 0, 0, 741, 742, 5, 76, 0, 0, 742, 743, 5, 102, 0, 0, 743, 744, 3, 114, 57, 0, 744, 745, 5, 103, 0, 0, 745, 766, 1, 0, 0, 0, 746, 747, 5, 77, 0, 0, 747, 748, 5, 102, 0, 0, 748, 749, 3, 98, 49, 0, 749, 750, 5, 103, 0, 0, 750, 766, 1, 0, 0, 0, 751, 752, 5, 78, 0, 0, 752, 753, 5, 102, 0, 0, 753, 754, 3, 98, 49, 0, 754, 755, 5, 103, 0, 0, 755, 766, 1, 0, 0, 0, 756, 757, 5, 79, 0, 0, 757, 761, 5, 96, 0, 0, 758, 760, 3, 100, 50, 0, 759, 758, 1, 0, 0, 0, 760, 763, 1, 0, 0, 0, 761, 759, 1, 0, 0, 0, 761, 762, 1, 0, 0, 0, 762, 764, 1, 0, 0, 0, 763, 761, 1, 0, 0, 0, 764, 766, 5, 97, 0, 0, 765, 731, 1, 0, 0, 0, 765, 732, 1, 0, 0, 0, 765, 733, 1, 0, 0, 0, 765, 734, 1, 0, 0, 0, 765, 736, 1, 0, 0, 0, 765, 741, 1, 0, 0, 0, 765, 746, 1, 0, 0, 0, 765, 751, 1, 0, 0, 0, 765, 756, 1, 0, 0, 0, 766, 99, 1, 0, 0, 0, 767, 768, 3, 114, 57, 0, 768, 769, 5, 92, 0, 0, 769, 770, 3, 98, 49, 0, 770, 771, 5, 93, 0, 0, 771, 101, 1, 0, 0, 0, 772, 773, 7, 4, 0, 0, 773, 103, 1, 0, 0, 0, 774, 775, 7, 5, 0, 0, 775, 105, 1, 0, 0, 0, 776, 785, 3, 116, 58, 0, 777, 785, 5, 104, 0, 0, 778, 785, 5, 105, 0, 0, 779, 785, 5, 88, 0, 0, 780, 785, 5, 89, 0, 0, 781, 785, 5, 90, 0, 0, 782, 785, 3, 108, 54, 0, 783, 785, 3, 112, 56, 0, 784, 776, 1, 0, 0, 0, 784, 777, 1, 0, 0, 0, 784, 778, 1, 0, 0, 0, 784, 779, 1, 0, 0, 0, 784, 780, 1, 0, 0, 0, 784, 781, 1, 0, 0, 0, 784, 782, 1, 0, 0, 0, 784, 783, 1, 0, 0, 0, 785, 107, 1, 0, 0, 0, 786, 795, 5, 96, 0, 0, 787, 792, 3, 110, 55, 0, 788, 789, 5, 94, 0, 0, 789, 791, 3, 110, 55, 0, 790, 788, 1, 0, 0, 0, 791, 794, 1, 0, 0, 0, 792, 790, 1, 0, 0, 0, 792, 793, 1, 0, 0, 0, 793, 796, 1, 0, 0, 0, 794, 792, 1, 0, 0, 0, 795, 787, 1, 0, 0, 0, 795, 796, 1, 0, 0, 0, 796, 797, 1, 0, 0, 0, 797, 798, 5, 97, 0, 0, 798, 109, 1, 0, 0, 0, 799, 800, 3, 116, 58, 0, 800, 801, 5, 92, 0, 0, 801, 802, 3, 106, 53, 0, 802, 111, 1, 0, 0, 0, 803, 812, 5, 98, 0, 0, 804, 809, 3, 106, 53, 0, 805, 806, 5, 94, 0, 0, 806, 808, 3, 106, 53, 0, 807, 805, 1, 0, 0, 0, 808, 811, 1, 0, 0, 0, 809, 807, 1, 0, 0, 0, 809, 810, 1, 0, 0, 0, 810, 813, 1, 0, 0, 0, 811, 809, 1, 0, 0, 0, 812, 804, 1, 0, 0, 0, 812, 813, 1, 0, 0, 0, 813, 814, 1, 0, 0, 0, 814, 815, 5, 99, 0, 0, 815, 113, 1, 0, 0, 0, 816, 817, 7, 6, 0, 0, 817, 115, 1, 0, 0, 0, 818, 819, 5, 107, 0, 0, 819, 117, 1, 0, 0, 0, 59, 130, 137, 158, 169, 181, 200, 208, 215, 228, 236, 263, 287, 297, 303, 332, 338, 343, 354, 360, 368, 381, 386, 399, 412, 427, 430, 437, 445, 485, 489, 497, 509, 523, 533, 551, 555, 558, 562, 565, 575, 579, 585, 594, 631, 650, 652, 663, 678, 694, 708, 718, 727, 761, 765, 784, 792, 795, 809, 812] \ No newline at end of file +[4, 1, 117, 958, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 3, 0, 143, 8, 0, 1, 1, 1, 1, 1, 1, 5, 1, 148, 8, 1, 10, 1, 12, 1, 151, 9, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 5, 3, 169, 8, 3, 10, 3, 12, 3, 172, 9, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 3, 4, 183, 8, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 195, 8, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 215, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 223, 8, 9, 1, 9, 1, 9, 1, 10, 5, 10, 228, 8, 10, 10, 10, 12, 10, 231, 9, 10, 1, 10, 1, 10, 1, 10, 3, 10, 236, 8, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 5, 10, 246, 8, 10, 10, 10, 12, 10, 249, 9, 10, 3, 10, 251, 8, 10, 1, 10, 1, 10, 5, 10, 255, 8, 10, 10, 10, 12, 10, 258, 9, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 5, 11, 266, 8, 11, 10, 11, 12, 11, 269, 9, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 3, 12, 277, 8, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 5, 12, 285, 8, 12, 10, 12, 12, 12, 288, 9, 12, 3, 12, 290, 8, 12, 3, 12, 292, 8, 12, 1, 13, 1, 13, 3, 13, 296, 8, 13, 1, 14, 1, 14, 1, 14, 1, 14, 5, 14, 302, 8, 14, 10, 14, 12, 14, 305, 9, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 3, 15, 316, 8, 15, 1, 16, 1, 16, 1, 16, 3, 16, 321, 8, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 3, 17, 330, 8, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 5, 19, 355, 8, 19, 10, 19, 12, 19, 358, 9, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 3, 20, 381, 8, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 3, 21, 391, 8, 21, 1, 21, 1, 21, 5, 21, 395, 8, 21, 10, 21, 12, 21, 398, 9, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 3, 22, 426, 8, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 3, 23, 433, 8, 23, 1, 23, 1, 23, 3, 23, 437, 8, 23, 1, 24, 5, 24, 440, 8, 24, 10, 24, 12, 24, 443, 9, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 3, 24, 453, 8, 24, 1, 24, 1, 24, 5, 24, 457, 8, 24, 10, 24, 12, 24, 460, 9, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 3, 25, 467, 8, 25, 1, 26, 1, 26, 1, 26, 3, 26, 472, 8, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 3, 26, 483, 8, 26, 1, 26, 1, 26, 1, 26, 3, 26, 488, 8, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 3, 27, 495, 8, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 3, 27, 504, 8, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 3, 28, 517, 8, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 5, 31, 536, 8, 31, 10, 31, 12, 31, 539, 9, 31, 3, 31, 541, 8, 31, 1, 31, 3, 31, 544, 8, 31, 1, 32, 1, 32, 1, 32, 5, 32, 549, 8, 32, 10, 32, 12, 32, 552, 9, 32, 1, 33, 1, 33, 1, 33, 5, 33, 557, 8, 33, 10, 33, 12, 33, 560, 9, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 3, 34, 590, 8, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 3, 34, 602, 8, 34, 1, 34, 1, 34, 3, 34, 606, 8, 34, 1, 35, 1, 35, 1, 35, 1, 35, 5, 35, 612, 8, 35, 10, 35, 12, 35, 615, 9, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 3, 38, 626, 8, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 3, 39, 640, 8, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 3, 40, 650, 8, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 3, 42, 668, 8, 42, 1, 42, 1, 42, 3, 42, 672, 8, 42, 1, 42, 3, 42, 675, 8, 42, 1, 42, 1, 42, 3, 42, 679, 8, 42, 1, 42, 3, 42, 682, 8, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 3, 43, 691, 8, 43, 1, 43, 1, 43, 3, 43, 695, 8, 43, 1, 43, 1, 43, 3, 43, 699, 8, 43, 1, 43, 1, 43, 5, 43, 703, 8, 43, 10, 43, 12, 43, 706, 9, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 3, 44, 714, 8, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 3, 48, 751, 8, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 3, 49, 770, 8, 49, 1, 49, 3, 49, 773, 8, 49, 3, 49, 775, 8, 49, 1, 50, 1, 50, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 5, 52, 784, 8, 52, 10, 52, 12, 52, 787, 9, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 3, 53, 801, 8, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 3, 53, 817, 8, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 3, 53, 826, 8, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 3, 53, 834, 8, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 3, 53, 844, 8, 53, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 853, 8, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 871, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 5, 55, 893, 8, 55, 10, 55, 12, 55, 896, 9, 55, 1, 55, 1, 55, 1, 55, 3, 55, 901, 8, 55, 3, 55, 903, 8, 55, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 922, 8, 59, 1, 60, 1, 60, 1, 60, 1, 60, 5, 60, 928, 8, 60, 10, 60, 12, 60, 931, 9, 60, 3, 60, 933, 8, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 1, 62, 1, 62, 5, 62, 945, 8, 62, 10, 62, 12, 62, 948, 9, 62, 3, 62, 950, 8, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 64, 1, 64, 1, 64, 0, 0, 65, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 0, 7, 1, 0, 65, 69, 2, 0, 60, 64, 66, 67, 2, 0, 60, 61, 66, 67, 2, 0, 62, 64, 66, 67, 1, 0, 85, 92, 1, 0, 72, 75, 2, 0, 39, 39, 113, 113, 1022, 0, 142, 1, 0, 0, 0, 2, 144, 1, 0, 0, 0, 4, 154, 1, 0, 0, 0, 6, 158, 1, 0, 0, 0, 8, 182, 1, 0, 0, 0, 10, 194, 1, 0, 0, 0, 12, 196, 1, 0, 0, 0, 14, 203, 1, 0, 0, 0, 16, 214, 1, 0, 0, 0, 18, 216, 1, 0, 0, 0, 20, 229, 1, 0, 0, 0, 22, 261, 1, 0, 0, 0, 24, 291, 1, 0, 0, 0, 26, 293, 1, 0, 0, 0, 28, 297, 1, 0, 0, 0, 30, 315, 1, 0, 0, 0, 32, 317, 1, 0, 0, 0, 34, 329, 1, 0, 0, 0, 36, 331, 1, 0, 0, 0, 38, 346, 1, 0, 0, 0, 40, 380, 1, 0, 0, 0, 42, 382, 1, 0, 0, 0, 44, 425, 1, 0, 0, 0, 46, 436, 1, 0, 0, 0, 48, 441, 1, 0, 0, 0, 50, 466, 1, 0, 0, 0, 52, 468, 1, 0, 0, 0, 54, 491, 1, 0, 0, 0, 56, 507, 1, 0, 0, 0, 58, 520, 1, 0, 0, 0, 60, 523, 1, 0, 0, 0, 62, 543, 1, 0, 0, 0, 64, 545, 1, 0, 0, 0, 66, 553, 1, 0, 0, 0, 68, 605, 1, 0, 0, 0, 70, 607, 1, 0, 0, 0, 72, 618, 1, 0, 0, 0, 74, 620, 1, 0, 0, 0, 76, 625, 1, 0, 0, 0, 78, 627, 1, 0, 0, 0, 80, 649, 1, 0, 0, 0, 82, 651, 1, 0, 0, 0, 84, 660, 1, 0, 0, 0, 86, 685, 1, 0, 0, 0, 88, 713, 1, 0, 0, 0, 90, 715, 1, 0, 0, 0, 92, 729, 1, 0, 0, 0, 94, 735, 1, 0, 0, 0, 96, 750, 1, 0, 0, 0, 98, 774, 1, 0, 0, 0, 100, 776, 1, 0, 0, 0, 102, 778, 1, 0, 0, 0, 104, 780, 1, 0, 0, 0, 106, 843, 1, 0, 0, 0, 108, 845, 1, 0, 0, 0, 110, 902, 1, 0, 0, 0, 112, 904, 1, 0, 0, 0, 114, 909, 1, 0, 0, 0, 116, 911, 1, 0, 0, 0, 118, 921, 1, 0, 0, 0, 120, 923, 1, 0, 0, 0, 122, 936, 1, 0, 0, 0, 124, 940, 1, 0, 0, 0, 126, 953, 1, 0, 0, 0, 128, 955, 1, 0, 0, 0, 130, 131, 3, 6, 3, 0, 131, 132, 5, 0, 0, 1, 132, 143, 1, 0, 0, 0, 133, 134, 3, 20, 10, 0, 134, 135, 5, 0, 0, 1, 135, 143, 1, 0, 0, 0, 136, 137, 3, 48, 24, 0, 137, 138, 5, 0, 0, 1, 138, 143, 1, 0, 0, 0, 139, 140, 3, 2, 1, 0, 140, 141, 5, 0, 0, 1, 141, 143, 1, 0, 0, 0, 142, 130, 1, 0, 0, 0, 142, 133, 1, 0, 0, 0, 142, 136, 1, 0, 0, 0, 142, 139, 1, 0, 0, 0, 143, 1, 1, 0, 0, 0, 144, 145, 5, 7, 0, 0, 145, 149, 5, 101, 0, 0, 146, 148, 3, 8, 4, 0, 147, 146, 1, 0, 0, 0, 148, 151, 1, 0, 0, 0, 149, 147, 1, 0, 0, 0, 149, 150, 1, 0, 0, 0, 150, 152, 1, 0, 0, 0, 151, 149, 1, 0, 0, 0, 152, 153, 5, 102, 0, 0, 153, 3, 1, 0, 0, 0, 154, 155, 5, 8, 0, 0, 155, 156, 3, 128, 64, 0, 156, 157, 5, 98, 0, 0, 157, 5, 1, 0, 0, 0, 158, 159, 5, 1, 0, 0, 159, 160, 3, 126, 63, 0, 160, 161, 5, 48, 0, 0, 161, 162, 3, 128, 64, 0, 162, 163, 5, 42, 0, 0, 163, 164, 3, 128, 64, 0, 164, 165, 5, 41, 0, 0, 165, 166, 3, 128, 64, 0, 166, 170, 5, 101, 0, 0, 167, 169, 3, 8, 4, 0, 168, 167, 1, 0, 0, 0, 169, 172, 1, 0, 0, 0, 170, 168, 1, 0, 0, 0, 170, 171, 1, 0, 0, 0, 171, 173, 1, 0, 0, 0, 172, 170, 1, 0, 0, 0, 173, 174, 5, 102, 0, 0, 174, 7, 1, 0, 0, 0, 175, 183, 3, 4, 2, 0, 176, 183, 3, 18, 9, 0, 177, 183, 3, 10, 5, 0, 178, 183, 3, 74, 37, 0, 179, 183, 3, 86, 43, 0, 180, 183, 3, 108, 54, 0, 181, 183, 3, 32, 16, 0, 182, 175, 1, 0, 0, 0, 182, 176, 1, 0, 0, 0, 182, 177, 1, 0, 0, 0, 182, 178, 1, 0, 0, 0, 182, 179, 1, 0, 0, 0, 182, 180, 1, 0, 0, 0, 182, 181, 1, 0, 0, 0, 183, 9, 1, 0, 0, 0, 184, 185, 5, 8, 0, 0, 185, 186, 5, 11, 0, 0, 186, 187, 3, 126, 63, 0, 187, 188, 5, 98, 0, 0, 188, 195, 1, 0, 0, 0, 189, 190, 5, 8, 0, 0, 190, 191, 5, 13, 0, 0, 191, 192, 3, 126, 63, 0, 192, 193, 5, 98, 0, 0, 193, 195, 1, 0, 0, 0, 194, 184, 1, 0, 0, 0, 194, 189, 1, 0, 0, 0, 195, 11, 1, 0, 0, 0, 196, 197, 5, 9, 0, 0, 197, 198, 5, 10, 0, 0, 198, 199, 3, 126, 63, 0, 199, 200, 5, 48, 0, 0, 200, 201, 3, 128, 64, 0, 201, 202, 5, 98, 0, 0, 202, 13, 1, 0, 0, 0, 203, 204, 5, 9, 0, 0, 204, 205, 5, 11, 0, 0, 205, 206, 3, 126, 63, 0, 206, 207, 5, 42, 0, 0, 207, 208, 3, 128, 64, 0, 208, 209, 5, 98, 0, 0, 209, 15, 1, 0, 0, 0, 210, 215, 3, 10, 5, 0, 211, 215, 3, 12, 6, 0, 212, 215, 3, 14, 7, 0, 213, 215, 3, 32, 16, 0, 214, 210, 1, 0, 0, 0, 214, 211, 1, 0, 0, 0, 214, 212, 1, 0, 0, 0, 214, 213, 1, 0, 0, 0, 215, 17, 1, 0, 0, 0, 216, 217, 5, 10, 0, 0, 217, 218, 3, 126, 63, 0, 218, 219, 5, 48, 0, 0, 219, 222, 3, 128, 64, 0, 220, 221, 5, 49, 0, 0, 221, 223, 3, 128, 64, 0, 222, 220, 1, 0, 0, 0, 222, 223, 1, 0, 0, 0, 223, 224, 1, 0, 0, 0, 224, 225, 5, 98, 0, 0, 225, 19, 1, 0, 0, 0, 226, 228, 3, 16, 8, 0, 227, 226, 1, 0, 0, 0, 228, 231, 1, 0, 0, 0, 229, 227, 1, 0, 0, 0, 229, 230, 1, 0, 0, 0, 230, 232, 1, 0, 0, 0, 231, 229, 1, 0, 0, 0, 232, 233, 5, 11, 0, 0, 233, 235, 3, 126, 63, 0, 234, 236, 3, 22, 11, 0, 235, 234, 1, 0, 0, 0, 235, 236, 1, 0, 0, 0, 236, 237, 1, 0, 0, 0, 237, 238, 5, 48, 0, 0, 238, 239, 3, 128, 64, 0, 239, 240, 5, 42, 0, 0, 240, 250, 3, 128, 64, 0, 241, 242, 5, 53, 0, 0, 242, 247, 3, 26, 13, 0, 243, 244, 5, 99, 0, 0, 244, 246, 3, 26, 13, 0, 245, 243, 1, 0, 0, 0, 246, 249, 1, 0, 0, 0, 247, 245, 1, 0, 0, 0, 247, 248, 1, 0, 0, 0, 248, 251, 1, 0, 0, 0, 249, 247, 1, 0, 0, 0, 250, 241, 1, 0, 0, 0, 250, 251, 1, 0, 0, 0, 251, 252, 1, 0, 0, 0, 252, 256, 5, 101, 0, 0, 253, 255, 3, 34, 17, 0, 254, 253, 1, 0, 0, 0, 255, 258, 1, 0, 0, 0, 256, 254, 1, 0, 0, 0, 256, 257, 1, 0, 0, 0, 257, 259, 1, 0, 0, 0, 258, 256, 1, 0, 0, 0, 259, 260, 5, 102, 0, 0, 260, 21, 1, 0, 0, 0, 261, 262, 5, 107, 0, 0, 262, 267, 3, 24, 12, 0, 263, 264, 5, 99, 0, 0, 264, 266, 3, 24, 12, 0, 265, 263, 1, 0, 0, 0, 266, 269, 1, 0, 0, 0, 267, 265, 1, 0, 0, 0, 267, 268, 1, 0, 0, 0, 268, 270, 1, 0, 0, 0, 269, 267, 1, 0, 0, 0, 270, 271, 5, 108, 0, 0, 271, 23, 1, 0, 0, 0, 272, 273, 5, 14, 0, 0, 273, 276, 3, 126, 63, 0, 274, 275, 5, 97, 0, 0, 275, 277, 5, 4, 0, 0, 276, 274, 1, 0, 0, 0, 276, 277, 1, 0, 0, 0, 277, 292, 1, 0, 0, 0, 278, 279, 5, 3, 0, 0, 279, 289, 3, 126, 63, 0, 280, 281, 5, 5, 0, 0, 281, 286, 3, 26, 13, 0, 282, 283, 5, 109, 0, 0, 283, 285, 3, 26, 13, 0, 284, 282, 1, 0, 0, 0, 285, 288, 1, 0, 0, 0, 286, 284, 1, 0, 0, 0, 286, 287, 1, 0, 0, 0, 287, 290, 1, 0, 0, 0, 288, 286, 1, 0, 0, 0, 289, 280, 1, 0, 0, 0, 289, 290, 1, 0, 0, 0, 290, 292, 1, 0, 0, 0, 291, 272, 1, 0, 0, 0, 291, 278, 1, 0, 0, 0, 292, 25, 1, 0, 0, 0, 293, 295, 3, 126, 63, 0, 294, 296, 3, 28, 14, 0, 295, 294, 1, 0, 0, 0, 295, 296, 1, 0, 0, 0, 296, 27, 1, 0, 0, 0, 297, 298, 5, 107, 0, 0, 298, 303, 3, 30, 15, 0, 299, 300, 5, 99, 0, 0, 300, 302, 3, 30, 15, 0, 301, 299, 1, 0, 0, 0, 302, 305, 1, 0, 0, 0, 303, 301, 1, 0, 0, 0, 303, 304, 1, 0, 0, 0, 304, 306, 1, 0, 0, 0, 305, 303, 1, 0, 0, 0, 306, 307, 5, 108, 0, 0, 307, 29, 1, 0, 0, 0, 308, 309, 5, 10, 0, 0, 309, 316, 3, 126, 63, 0, 310, 311, 5, 11, 0, 0, 311, 316, 3, 26, 13, 0, 312, 313, 5, 3, 0, 0, 313, 316, 3, 126, 63, 0, 314, 316, 3, 110, 55, 0, 315, 308, 1, 0, 0, 0, 315, 310, 1, 0, 0, 0, 315, 312, 1, 0, 0, 0, 315, 314, 1, 0, 0, 0, 316, 31, 1, 0, 0, 0, 317, 318, 5, 2, 0, 0, 318, 320, 3, 126, 63, 0, 319, 321, 3, 22, 11, 0, 320, 319, 1, 0, 0, 0, 320, 321, 1, 0, 0, 0, 321, 322, 1, 0, 0, 0, 322, 323, 5, 110, 0, 0, 323, 324, 3, 110, 55, 0, 324, 325, 5, 98, 0, 0, 325, 33, 1, 0, 0, 0, 326, 330, 3, 38, 19, 0, 327, 330, 3, 42, 21, 0, 328, 330, 3, 36, 18, 0, 329, 326, 1, 0, 0, 0, 329, 327, 1, 0, 0, 0, 329, 328, 1, 0, 0, 0, 330, 35, 1, 0, 0, 0, 331, 332, 5, 16, 0, 0, 332, 333, 3, 126, 63, 0, 333, 334, 5, 48, 0, 0, 334, 335, 3, 128, 64, 0, 335, 336, 5, 97, 0, 0, 336, 337, 3, 110, 55, 0, 337, 338, 5, 96, 0, 0, 338, 339, 3, 110, 55, 0, 339, 340, 5, 101, 0, 0, 340, 341, 5, 65, 0, 0, 341, 342, 5, 48, 0, 0, 342, 343, 3, 128, 64, 0, 343, 344, 5, 98, 0, 0, 344, 345, 5, 102, 0, 0, 345, 37, 1, 0, 0, 0, 346, 347, 5, 14, 0, 0, 347, 348, 3, 126, 63, 0, 348, 349, 5, 48, 0, 0, 349, 350, 3, 128, 64, 0, 350, 351, 5, 97, 0, 0, 351, 352, 3, 110, 55, 0, 352, 356, 5, 101, 0, 0, 353, 355, 3, 40, 20, 0, 354, 353, 1, 0, 0, 0, 355, 358, 1, 0, 0, 0, 356, 354, 1, 0, 0, 0, 356, 357, 1, 0, 0, 0, 357, 359, 1, 0, 0, 0, 358, 356, 1, 0, 0, 0, 359, 360, 5, 102, 0, 0, 360, 39, 1, 0, 0, 0, 361, 362, 5, 55, 0, 0, 362, 363, 5, 48, 0, 0, 363, 364, 3, 128, 64, 0, 364, 365, 5, 98, 0, 0, 365, 381, 1, 0, 0, 0, 366, 367, 5, 56, 0, 0, 367, 368, 5, 48, 0, 0, 368, 369, 3, 128, 64, 0, 369, 370, 5, 98, 0, 0, 370, 381, 1, 0, 0, 0, 371, 372, 5, 57, 0, 0, 372, 373, 5, 58, 0, 0, 373, 374, 5, 48, 0, 0, 374, 375, 3, 128, 64, 0, 375, 376, 5, 59, 0, 0, 376, 377, 5, 48, 0, 0, 377, 378, 3, 128, 64, 0, 378, 379, 5, 98, 0, 0, 379, 381, 1, 0, 0, 0, 380, 361, 1, 0, 0, 0, 380, 366, 1, 0, 0, 0, 380, 371, 1, 0, 0, 0, 381, 41, 1, 0, 0, 0, 382, 383, 5, 15, 0, 0, 383, 384, 3, 126, 63, 0, 384, 385, 5, 48, 0, 0, 385, 386, 3, 128, 64, 0, 386, 387, 5, 97, 0, 0, 387, 388, 3, 116, 58, 0, 388, 390, 3, 46, 23, 0, 389, 391, 5, 76, 0, 0, 390, 389, 1, 0, 0, 0, 390, 391, 1, 0, 0, 0, 391, 392, 1, 0, 0, 0, 392, 396, 5, 101, 0, 0, 393, 395, 3, 44, 22, 0, 394, 393, 1, 0, 0, 0, 395, 398, 1, 0, 0, 0, 396, 394, 1, 0, 0, 0, 396, 397, 1, 0, 0, 0, 397, 399, 1, 0, 0, 0, 398, 396, 1, 0, 0, 0, 399, 400, 5, 102, 0, 0, 400, 43, 1, 0, 0, 0, 401, 402, 5, 62, 0, 0, 402, 403, 5, 48, 0, 0, 403, 404, 3, 128, 64, 0, 404, 405, 5, 98, 0, 0, 405, 426, 1, 0, 0, 0, 406, 407, 5, 63, 0, 0, 407, 408, 5, 48, 0, 0, 408, 409, 3, 128, 64, 0, 409, 410, 5, 98, 0, 0, 410, 426, 1, 0, 0, 0, 411, 412, 5, 64, 0, 0, 412, 413, 5, 48, 0, 0, 413, 414, 3, 128, 64, 0, 414, 415, 5, 98, 0, 0, 415, 426, 1, 0, 0, 0, 416, 417, 5, 57, 0, 0, 417, 418, 5, 58, 0, 0, 418, 419, 5, 48, 0, 0, 419, 420, 3, 128, 64, 0, 420, 421, 5, 59, 0, 0, 421, 422, 5, 48, 0, 0, 422, 423, 3, 128, 64, 0, 423, 424, 5, 98, 0, 0, 424, 426, 1, 0, 0, 0, 425, 401, 1, 0, 0, 0, 425, 406, 1, 0, 0, 0, 425, 411, 1, 0, 0, 0, 425, 416, 1, 0, 0, 0, 426, 45, 1, 0, 0, 0, 427, 428, 5, 10, 0, 0, 428, 437, 3, 126, 63, 0, 429, 430, 5, 11, 0, 0, 430, 432, 3, 126, 63, 0, 431, 433, 3, 28, 14, 0, 432, 431, 1, 0, 0, 0, 432, 433, 1, 0, 0, 0, 433, 437, 1, 0, 0, 0, 434, 435, 5, 3, 0, 0, 435, 437, 3, 126, 63, 0, 436, 427, 1, 0, 0, 0, 436, 429, 1, 0, 0, 0, 436, 434, 1, 0, 0, 0, 437, 47, 1, 0, 0, 0, 438, 440, 3, 16, 8, 0, 439, 438, 1, 0, 0, 0, 440, 443, 1, 0, 0, 0, 441, 439, 1, 0, 0, 0, 441, 442, 1, 0, 0, 0, 442, 444, 1, 0, 0, 0, 443, 441, 1, 0, 0, 0, 444, 445, 5, 13, 0, 0, 445, 446, 3, 126, 63, 0, 446, 447, 5, 48, 0, 0, 447, 448, 3, 128, 64, 0, 448, 449, 5, 42, 0, 0, 449, 452, 3, 128, 64, 0, 450, 451, 5, 43, 0, 0, 451, 453, 5, 111, 0, 0, 452, 450, 1, 0, 0, 0, 452, 453, 1, 0, 0, 0, 453, 454, 1, 0, 0, 0, 454, 458, 5, 101, 0, 0, 455, 457, 3, 50, 25, 0, 456, 455, 1, 0, 0, 0, 457, 460, 1, 0, 0, 0, 458, 456, 1, 0, 0, 0, 458, 459, 1, 0, 0, 0, 459, 461, 1, 0, 0, 0, 460, 458, 1, 0, 0, 0, 461, 462, 5, 102, 0, 0, 462, 49, 1, 0, 0, 0, 463, 467, 3, 52, 26, 0, 464, 467, 3, 54, 27, 0, 465, 467, 3, 56, 28, 0, 466, 463, 1, 0, 0, 0, 466, 464, 1, 0, 0, 0, 466, 465, 1, 0, 0, 0, 467, 51, 1, 0, 0, 0, 468, 469, 5, 16, 0, 0, 469, 471, 3, 126, 63, 0, 470, 472, 3, 22, 11, 0, 471, 470, 1, 0, 0, 0, 471, 472, 1, 0, 0, 0, 472, 473, 1, 0, 0, 0, 473, 474, 5, 48, 0, 0, 474, 475, 3, 128, 64, 0, 475, 476, 5, 97, 0, 0, 476, 477, 3, 110, 55, 0, 477, 478, 5, 96, 0, 0, 478, 479, 3, 110, 55, 0, 479, 480, 5, 50, 0, 0, 480, 482, 3, 60, 30, 0, 481, 483, 3, 58, 29, 0, 482, 481, 1, 0, 0, 0, 482, 483, 1, 0, 0, 0, 483, 484, 1, 0, 0, 0, 484, 485, 5, 52, 0, 0, 485, 487, 3, 62, 31, 0, 486, 488, 3, 66, 33, 0, 487, 486, 1, 0, 0, 0, 487, 488, 1, 0, 0, 0, 488, 489, 1, 0, 0, 0, 489, 490, 5, 98, 0, 0, 490, 53, 1, 0, 0, 0, 491, 492, 5, 17, 0, 0, 492, 494, 3, 126, 63, 0, 493, 495, 3, 22, 11, 0, 494, 493, 1, 0, 0, 0, 494, 495, 1, 0, 0, 0, 495, 496, 1, 0, 0, 0, 496, 497, 5, 48, 0, 0, 497, 498, 3, 128, 64, 0, 498, 499, 5, 97, 0, 0, 499, 500, 3, 110, 55, 0, 500, 501, 5, 96, 0, 0, 501, 503, 3, 110, 55, 0, 502, 504, 3, 66, 33, 0, 503, 502, 1, 0, 0, 0, 503, 504, 1, 0, 0, 0, 504, 505, 1, 0, 0, 0, 505, 506, 5, 98, 0, 0, 506, 55, 1, 0, 0, 0, 507, 508, 5, 18, 0, 0, 508, 509, 3, 126, 63, 0, 509, 510, 5, 48, 0, 0, 510, 511, 3, 128, 64, 0, 511, 512, 5, 19, 0, 0, 512, 513, 3, 126, 63, 0, 513, 514, 5, 97, 0, 0, 514, 516, 3, 110, 55, 0, 515, 517, 3, 66, 33, 0, 516, 515, 1, 0, 0, 0, 516, 517, 1, 0, 0, 0, 517, 518, 1, 0, 0, 0, 518, 519, 5, 98, 0, 0, 519, 57, 1, 0, 0, 0, 520, 521, 5, 51, 0, 0, 521, 522, 3, 110, 55, 0, 522, 59, 1, 0, 0, 0, 523, 524, 7, 0, 0, 0, 524, 61, 1, 0, 0, 0, 525, 544, 5, 54, 0, 0, 526, 527, 5, 10, 0, 0, 527, 544, 3, 126, 63, 0, 528, 529, 5, 3, 0, 0, 529, 544, 3, 126, 63, 0, 530, 531, 5, 12, 0, 0, 531, 540, 5, 103, 0, 0, 532, 537, 3, 26, 13, 0, 533, 534, 5, 99, 0, 0, 534, 536, 3, 26, 13, 0, 535, 533, 1, 0, 0, 0, 536, 539, 1, 0, 0, 0, 537, 535, 1, 0, 0, 0, 537, 538, 1, 0, 0, 0, 538, 541, 1, 0, 0, 0, 539, 537, 1, 0, 0, 0, 540, 532, 1, 0, 0, 0, 540, 541, 1, 0, 0, 0, 541, 542, 1, 0, 0, 0, 542, 544, 5, 104, 0, 0, 543, 525, 1, 0, 0, 0, 543, 526, 1, 0, 0, 0, 543, 528, 1, 0, 0, 0, 543, 530, 1, 0, 0, 0, 544, 63, 1, 0, 0, 0, 545, 550, 3, 126, 63, 0, 546, 547, 5, 99, 0, 0, 547, 549, 3, 126, 63, 0, 548, 546, 1, 0, 0, 0, 549, 552, 1, 0, 0, 0, 550, 548, 1, 0, 0, 0, 550, 551, 1, 0, 0, 0, 551, 65, 1, 0, 0, 0, 552, 550, 1, 0, 0, 0, 553, 554, 5, 53, 0, 0, 554, 558, 5, 101, 0, 0, 555, 557, 3, 68, 34, 0, 556, 555, 1, 0, 0, 0, 557, 560, 1, 0, 0, 0, 558, 556, 1, 0, 0, 0, 558, 559, 1, 0, 0, 0, 559, 561, 1, 0, 0, 0, 560, 558, 1, 0, 0, 0, 561, 562, 5, 102, 0, 0, 562, 67, 1, 0, 0, 0, 563, 564, 5, 27, 0, 0, 564, 565, 3, 126, 63, 0, 565, 566, 5, 48, 0, 0, 566, 567, 3, 128, 64, 0, 567, 568, 5, 97, 0, 0, 568, 569, 3, 110, 55, 0, 569, 570, 3, 70, 35, 0, 570, 571, 5, 98, 0, 0, 571, 606, 1, 0, 0, 0, 572, 573, 5, 28, 0, 0, 573, 574, 3, 126, 63, 0, 574, 575, 5, 48, 0, 0, 575, 576, 3, 128, 64, 0, 576, 577, 5, 97, 0, 0, 577, 578, 3, 116, 58, 0, 578, 579, 3, 46, 23, 0, 579, 580, 3, 70, 35, 0, 580, 581, 5, 98, 0, 0, 581, 606, 1, 0, 0, 0, 582, 583, 5, 11, 0, 0, 583, 584, 3, 126, 63, 0, 584, 585, 5, 48, 0, 0, 585, 586, 3, 128, 64, 0, 586, 587, 5, 97, 0, 0, 587, 589, 3, 126, 63, 0, 588, 590, 3, 28, 14, 0, 589, 588, 1, 0, 0, 0, 589, 590, 1, 0, 0, 0, 590, 591, 1, 0, 0, 0, 591, 592, 5, 98, 0, 0, 592, 606, 1, 0, 0, 0, 593, 594, 5, 18, 0, 0, 594, 595, 3, 126, 63, 0, 595, 596, 5, 48, 0, 0, 596, 597, 3, 128, 64, 0, 597, 598, 5, 97, 0, 0, 598, 601, 3, 126, 63, 0, 599, 600, 5, 20, 0, 0, 600, 602, 3, 110, 55, 0, 601, 599, 1, 0, 0, 0, 601, 602, 1, 0, 0, 0, 602, 603, 1, 0, 0, 0, 603, 604, 5, 98, 0, 0, 604, 606, 1, 0, 0, 0, 605, 563, 1, 0, 0, 0, 605, 572, 1, 0, 0, 0, 605, 582, 1, 0, 0, 0, 605, 593, 1, 0, 0, 0, 606, 69, 1, 0, 0, 0, 607, 608, 5, 103, 0, 0, 608, 613, 3, 72, 36, 0, 609, 610, 5, 99, 0, 0, 610, 612, 3, 72, 36, 0, 611, 609, 1, 0, 0, 0, 612, 615, 1, 0, 0, 0, 613, 611, 1, 0, 0, 0, 613, 614, 1, 0, 0, 0, 614, 616, 1, 0, 0, 0, 615, 613, 1, 0, 0, 0, 616, 617, 5, 104, 0, 0, 617, 71, 1, 0, 0, 0, 618, 619, 7, 1, 0, 0, 619, 73, 1, 0, 0, 0, 620, 621, 5, 26, 0, 0, 621, 622, 3, 76, 38, 0, 622, 75, 1, 0, 0, 0, 623, 626, 3, 78, 39, 0, 624, 626, 3, 82, 41, 0, 625, 623, 1, 0, 0, 0, 625, 624, 1, 0, 0, 0, 626, 77, 1, 0, 0, 0, 627, 628, 5, 27, 0, 0, 628, 629, 3, 126, 63, 0, 629, 630, 5, 48, 0, 0, 630, 631, 3, 128, 64, 0, 631, 632, 5, 36, 0, 0, 632, 633, 3, 126, 63, 0, 633, 634, 5, 97, 0, 0, 634, 635, 3, 110, 55, 0, 635, 636, 5, 37, 0, 0, 636, 639, 3, 80, 40, 0, 637, 638, 5, 38, 0, 0, 638, 640, 3, 118, 59, 0, 639, 637, 1, 0, 0, 0, 639, 640, 1, 0, 0, 0, 640, 641, 1, 0, 0, 0, 641, 642, 5, 98, 0, 0, 642, 79, 1, 0, 0, 0, 643, 650, 5, 70, 0, 0, 644, 645, 5, 71, 0, 0, 645, 646, 5, 105, 0, 0, 646, 647, 3, 110, 55, 0, 647, 648, 5, 106, 0, 0, 648, 650, 1, 0, 0, 0, 649, 643, 1, 0, 0, 0, 649, 644, 1, 0, 0, 0, 650, 81, 1, 0, 0, 0, 651, 652, 5, 28, 0, 0, 652, 653, 3, 126, 63, 0, 653, 654, 5, 48, 0, 0, 654, 655, 3, 128, 64, 0, 655, 656, 5, 101, 0, 0, 656, 657, 3, 84, 42, 0, 657, 658, 3, 84, 42, 0, 658, 659, 5, 102, 0, 0, 659, 83, 1, 0, 0, 0, 660, 661, 3, 46, 23, 0, 661, 662, 5, 29, 0, 0, 662, 663, 3, 126, 63, 0, 663, 664, 5, 48, 0, 0, 664, 665, 3, 128, 64, 0, 665, 667, 3, 116, 58, 0, 666, 668, 5, 76, 0, 0, 667, 666, 1, 0, 0, 0, 667, 668, 1, 0, 0, 0, 668, 671, 1, 0, 0, 0, 669, 670, 5, 44, 0, 0, 670, 672, 3, 128, 64, 0, 671, 669, 1, 0, 0, 0, 671, 672, 1, 0, 0, 0, 672, 674, 1, 0, 0, 0, 673, 675, 5, 45, 0, 0, 674, 673, 1, 0, 0, 0, 674, 675, 1, 0, 0, 0, 675, 678, 1, 0, 0, 0, 676, 677, 5, 46, 0, 0, 677, 679, 3, 128, 64, 0, 678, 676, 1, 0, 0, 0, 678, 679, 1, 0, 0, 0, 679, 681, 1, 0, 0, 0, 680, 682, 5, 47, 0, 0, 681, 680, 1, 0, 0, 0, 681, 682, 1, 0, 0, 0, 682, 683, 1, 0, 0, 0, 683, 684, 5, 98, 0, 0, 684, 85, 1, 0, 0, 0, 685, 686, 5, 21, 0, 0, 686, 687, 3, 126, 63, 0, 687, 688, 5, 22, 0, 0, 688, 690, 3, 126, 63, 0, 689, 691, 3, 28, 14, 0, 690, 689, 1, 0, 0, 0, 690, 691, 1, 0, 0, 0, 691, 694, 1, 0, 0, 0, 692, 693, 5, 48, 0, 0, 693, 695, 3, 128, 64, 0, 694, 692, 1, 0, 0, 0, 694, 695, 1, 0, 0, 0, 695, 698, 1, 0, 0, 0, 696, 697, 5, 43, 0, 0, 697, 699, 5, 111, 0, 0, 698, 696, 1, 0, 0, 0, 698, 699, 1, 0, 0, 0, 699, 700, 1, 0, 0, 0, 700, 704, 5, 101, 0, 0, 701, 703, 3, 88, 44, 0, 702, 701, 1, 0, 0, 0, 703, 706, 1, 0, 0, 0, 704, 702, 1, 0, 0, 0, 704, 705, 1, 0, 0, 0, 705, 707, 1, 0, 0, 0, 706, 704, 1, 0, 0, 0, 707, 708, 5, 102, 0, 0, 708, 87, 1, 0, 0, 0, 709, 710, 5, 25, 0, 0, 710, 714, 3, 76, 38, 0, 711, 714, 3, 92, 46, 0, 712, 714, 3, 90, 45, 0, 713, 709, 1, 0, 0, 0, 713, 711, 1, 0, 0, 0, 713, 712, 1, 0, 0, 0, 714, 89, 1, 0, 0, 0, 715, 716, 5, 33, 0, 0, 716, 717, 3, 126, 63, 0, 717, 718, 5, 34, 0, 0, 718, 719, 5, 35, 0, 0, 719, 720, 5, 31, 0, 0, 720, 721, 5, 18, 0, 0, 721, 722, 3, 126, 63, 0, 722, 723, 5, 32, 0, 0, 723, 724, 5, 28, 0, 0, 724, 725, 3, 126, 63, 0, 725, 726, 5, 100, 0, 0, 726, 727, 3, 126, 63, 0, 727, 728, 5, 98, 0, 0, 728, 91, 1, 0, 0, 0, 729, 730, 5, 23, 0, 0, 730, 731, 3, 94, 47, 0, 731, 732, 5, 24, 0, 0, 732, 733, 3, 98, 49, 0, 733, 734, 5, 98, 0, 0, 734, 93, 1, 0, 0, 0, 735, 736, 3, 126, 63, 0, 736, 737, 5, 100, 0, 0, 737, 738, 3, 96, 48, 0, 738, 95, 1, 0, 0, 0, 739, 751, 3, 126, 63, 0, 740, 751, 5, 65, 0, 0, 741, 751, 5, 55, 0, 0, 742, 751, 5, 56, 0, 0, 743, 751, 5, 62, 0, 0, 744, 751, 5, 63, 0, 0, 745, 751, 5, 64, 0, 0, 746, 751, 5, 66, 0, 0, 747, 751, 5, 67, 0, 0, 748, 751, 5, 68, 0, 0, 749, 751, 5, 69, 0, 0, 750, 739, 1, 0, 0, 0, 750, 740, 1, 0, 0, 0, 750, 741, 1, 0, 0, 0, 750, 742, 1, 0, 0, 0, 750, 743, 1, 0, 0, 0, 750, 744, 1, 0, 0, 0, 750, 745, 1, 0, 0, 0, 750, 746, 1, 0, 0, 0, 750, 747, 1, 0, 0, 0, 750, 748, 1, 0, 0, 0, 750, 749, 1, 0, 0, 0, 751, 97, 1, 0, 0, 0, 752, 753, 5, 27, 0, 0, 753, 754, 3, 126, 63, 0, 754, 755, 5, 100, 0, 0, 755, 756, 3, 100, 50, 0, 756, 775, 1, 0, 0, 0, 757, 758, 5, 28, 0, 0, 758, 759, 3, 126, 63, 0, 759, 760, 5, 100, 0, 0, 760, 761, 3, 126, 63, 0, 761, 762, 5, 100, 0, 0, 762, 763, 3, 102, 51, 0, 763, 775, 1, 0, 0, 0, 764, 765, 5, 13, 0, 0, 765, 766, 3, 126, 63, 0, 766, 767, 5, 100, 0, 0, 767, 769, 3, 126, 63, 0, 768, 770, 3, 28, 14, 0, 769, 768, 1, 0, 0, 0, 769, 770, 1, 0, 0, 0, 770, 772, 1, 0, 0, 0, 771, 773, 3, 104, 52, 0, 772, 771, 1, 0, 0, 0, 772, 773, 1, 0, 0, 0, 773, 775, 1, 0, 0, 0, 774, 752, 1, 0, 0, 0, 774, 757, 1, 0, 0, 0, 774, 764, 1, 0, 0, 0, 775, 99, 1, 0, 0, 0, 776, 777, 7, 2, 0, 0, 777, 101, 1, 0, 0, 0, 778, 779, 7, 3, 0, 0, 779, 103, 1, 0, 0, 0, 780, 781, 5, 30, 0, 0, 781, 785, 5, 101, 0, 0, 782, 784, 3, 106, 53, 0, 783, 782, 1, 0, 0, 0, 784, 787, 1, 0, 0, 0, 785, 783, 1, 0, 0, 0, 785, 786, 1, 0, 0, 0, 786, 788, 1, 0, 0, 0, 787, 785, 1, 0, 0, 0, 788, 789, 5, 102, 0, 0, 789, 105, 1, 0, 0, 0, 790, 791, 3, 126, 63, 0, 791, 792, 5, 24, 0, 0, 792, 793, 5, 27, 0, 0, 793, 800, 3, 126, 63, 0, 794, 795, 5, 32, 0, 0, 795, 796, 5, 28, 0, 0, 796, 797, 3, 126, 63, 0, 797, 798, 5, 100, 0, 0, 798, 799, 3, 126, 63, 0, 799, 801, 1, 0, 0, 0, 800, 794, 1, 0, 0, 0, 800, 801, 1, 0, 0, 0, 801, 802, 1, 0, 0, 0, 802, 803, 5, 98, 0, 0, 803, 844, 1, 0, 0, 0, 804, 805, 3, 126, 63, 0, 805, 806, 5, 24, 0, 0, 806, 807, 5, 28, 0, 0, 807, 808, 3, 126, 63, 0, 808, 809, 5, 100, 0, 0, 809, 816, 3, 126, 63, 0, 810, 811, 5, 32, 0, 0, 811, 812, 5, 28, 0, 0, 812, 813, 3, 126, 63, 0, 813, 814, 5, 100, 0, 0, 814, 815, 3, 126, 63, 0, 815, 817, 1, 0, 0, 0, 816, 810, 1, 0, 0, 0, 816, 817, 1, 0, 0, 0, 817, 818, 1, 0, 0, 0, 818, 819, 5, 98, 0, 0, 819, 844, 1, 0, 0, 0, 820, 821, 3, 126, 63, 0, 821, 822, 5, 24, 0, 0, 822, 823, 5, 11, 0, 0, 823, 825, 3, 126, 63, 0, 824, 826, 3, 28, 14, 0, 825, 824, 1, 0, 0, 0, 825, 826, 1, 0, 0, 0, 826, 833, 1, 0, 0, 0, 827, 828, 5, 32, 0, 0, 828, 829, 5, 28, 0, 0, 829, 830, 3, 126, 63, 0, 830, 831, 5, 100, 0, 0, 831, 832, 3, 126, 63, 0, 832, 834, 1, 0, 0, 0, 833, 827, 1, 0, 0, 0, 833, 834, 1, 0, 0, 0, 834, 835, 1, 0, 0, 0, 835, 836, 5, 98, 0, 0, 836, 844, 1, 0, 0, 0, 837, 838, 3, 126, 63, 0, 838, 839, 5, 24, 0, 0, 839, 840, 5, 18, 0, 0, 840, 841, 3, 126, 63, 0, 841, 842, 5, 98, 0, 0, 842, 844, 1, 0, 0, 0, 843, 790, 1, 0, 0, 0, 843, 804, 1, 0, 0, 0, 843, 820, 1, 0, 0, 0, 843, 837, 1, 0, 0, 0, 844, 107, 1, 0, 0, 0, 845, 846, 5, 18, 0, 0, 846, 847, 3, 126, 63, 0, 847, 848, 5, 24, 0, 0, 848, 849, 3, 126, 63, 0, 849, 850, 5, 100, 0, 0, 850, 852, 3, 126, 63, 0, 851, 853, 3, 104, 52, 0, 852, 851, 1, 0, 0, 0, 852, 853, 1, 0, 0, 0, 853, 854, 1, 0, 0, 0, 854, 855, 5, 98, 0, 0, 855, 109, 1, 0, 0, 0, 856, 903, 3, 114, 57, 0, 857, 903, 5, 77, 0, 0, 858, 903, 5, 78, 0, 0, 859, 860, 5, 79, 0, 0, 860, 903, 3, 128, 64, 0, 861, 862, 5, 80, 0, 0, 862, 863, 5, 107, 0, 0, 863, 864, 3, 126, 63, 0, 864, 865, 5, 108, 0, 0, 865, 903, 1, 0, 0, 0, 866, 867, 5, 81, 0, 0, 867, 868, 5, 107, 0, 0, 868, 870, 3, 126, 63, 0, 869, 871, 3, 28, 14, 0, 870, 869, 1, 0, 0, 0, 870, 871, 1, 0, 0, 0, 871, 872, 1, 0, 0, 0, 872, 873, 5, 108, 0, 0, 873, 903, 1, 0, 0, 0, 874, 875, 5, 6, 0, 0, 875, 876, 5, 107, 0, 0, 876, 877, 3, 126, 63, 0, 877, 878, 5, 108, 0, 0, 878, 903, 1, 0, 0, 0, 879, 880, 5, 82, 0, 0, 880, 881, 5, 107, 0, 0, 881, 882, 3, 110, 55, 0, 882, 883, 5, 108, 0, 0, 883, 903, 1, 0, 0, 0, 884, 885, 5, 83, 0, 0, 885, 886, 5, 107, 0, 0, 886, 887, 3, 110, 55, 0, 887, 888, 5, 108, 0, 0, 888, 903, 1, 0, 0, 0, 889, 890, 5, 84, 0, 0, 890, 894, 5, 101, 0, 0, 891, 893, 3, 112, 56, 0, 892, 891, 1, 0, 0, 0, 893, 896, 1, 0, 0, 0, 894, 892, 1, 0, 0, 0, 894, 895, 1, 0, 0, 0, 895, 897, 1, 0, 0, 0, 896, 894, 1, 0, 0, 0, 897, 903, 5, 102, 0, 0, 898, 900, 3, 126, 63, 0, 899, 901, 3, 28, 14, 0, 900, 899, 1, 0, 0, 0, 900, 901, 1, 0, 0, 0, 901, 903, 1, 0, 0, 0, 902, 856, 1, 0, 0, 0, 902, 857, 1, 0, 0, 0, 902, 858, 1, 0, 0, 0, 902, 859, 1, 0, 0, 0, 902, 861, 1, 0, 0, 0, 902, 866, 1, 0, 0, 0, 902, 874, 1, 0, 0, 0, 902, 879, 1, 0, 0, 0, 902, 884, 1, 0, 0, 0, 902, 889, 1, 0, 0, 0, 902, 898, 1, 0, 0, 0, 903, 111, 1, 0, 0, 0, 904, 905, 3, 126, 63, 0, 905, 906, 5, 97, 0, 0, 906, 907, 3, 110, 55, 0, 907, 908, 5, 98, 0, 0, 908, 113, 1, 0, 0, 0, 909, 910, 7, 4, 0, 0, 910, 115, 1, 0, 0, 0, 911, 912, 7, 5, 0, 0, 912, 117, 1, 0, 0, 0, 913, 922, 3, 128, 64, 0, 914, 922, 5, 111, 0, 0, 915, 922, 5, 112, 0, 0, 916, 922, 5, 93, 0, 0, 917, 922, 5, 94, 0, 0, 918, 922, 5, 95, 0, 0, 919, 922, 3, 120, 60, 0, 920, 922, 3, 124, 62, 0, 921, 913, 1, 0, 0, 0, 921, 914, 1, 0, 0, 0, 921, 915, 1, 0, 0, 0, 921, 916, 1, 0, 0, 0, 921, 917, 1, 0, 0, 0, 921, 918, 1, 0, 0, 0, 921, 919, 1, 0, 0, 0, 921, 920, 1, 0, 0, 0, 922, 119, 1, 0, 0, 0, 923, 932, 5, 101, 0, 0, 924, 929, 3, 122, 61, 0, 925, 926, 5, 99, 0, 0, 926, 928, 3, 122, 61, 0, 927, 925, 1, 0, 0, 0, 928, 931, 1, 0, 0, 0, 929, 927, 1, 0, 0, 0, 929, 930, 1, 0, 0, 0, 930, 933, 1, 0, 0, 0, 931, 929, 1, 0, 0, 0, 932, 924, 1, 0, 0, 0, 932, 933, 1, 0, 0, 0, 933, 934, 1, 0, 0, 0, 934, 935, 5, 102, 0, 0, 935, 121, 1, 0, 0, 0, 936, 937, 3, 128, 64, 0, 937, 938, 5, 97, 0, 0, 938, 939, 3, 118, 59, 0, 939, 123, 1, 0, 0, 0, 940, 949, 5, 103, 0, 0, 941, 946, 3, 118, 59, 0, 942, 943, 5, 99, 0, 0, 943, 945, 3, 118, 59, 0, 944, 942, 1, 0, 0, 0, 945, 948, 1, 0, 0, 0, 946, 944, 1, 0, 0, 0, 946, 947, 1, 0, 0, 0, 947, 950, 1, 0, 0, 0, 948, 946, 1, 0, 0, 0, 949, 941, 1, 0, 0, 0, 949, 950, 1, 0, 0, 0, 950, 951, 1, 0, 0, 0, 951, 952, 5, 104, 0, 0, 952, 125, 1, 0, 0, 0, 953, 954, 7, 6, 0, 0, 954, 127, 1, 0, 0, 0, 955, 956, 5, 114, 0, 0, 956, 129, 1, 0, 0, 0, 81, 142, 149, 170, 182, 194, 214, 222, 229, 235, 247, 250, 256, 267, 276, 286, 289, 291, 295, 303, 315, 320, 329, 356, 380, 390, 396, 425, 432, 436, 441, 452, 458, 466, 471, 482, 487, 494, 503, 516, 537, 540, 543, 550, 558, 589, 601, 605, 613, 625, 639, 649, 667, 671, 674, 678, 681, 690, 694, 698, 704, 713, 750, 769, 772, 774, 785, 800, 816, 825, 833, 843, 852, 870, 894, 900, 902, 921, 929, 932, 946, 949] \ No newline at end of file diff --git a/src/capability-language/generated/QuixosCapability.tokens b/src/capability-language/generated/QuixosCapability.tokens index 8571c0b..75f42fb 100644 --- a/src/capability-language/generated/QuixosCapability.tokens +++ b/src/capability-language/generated/QuixosCapability.tokens @@ -1,213 +1,227 @@ WORKSPACE=1 -FRAGMENT=2 -IMPORT=3 -EXTERNAL=4 -ATOM=5 -INTERFACE=6 -INTERFACES=7 -PACKAGE=8 -VALUE=9 -RELATION=10 -OPERATION=11 -FUNCTION=12 -CONSTRUCTOR=13 -CONSTRUCTS=14 -INPUT=15 -CONFORM=16 -AS=17 -BIND=18 -TO=19 -PRIVATE=20 -SHARED=21 -STATE=22 -EDGE=23 -PROJECTION=24 -WITH=25 -USING=26 -VIA=27 -MATERIALIZE=28 -IF=29 -ABSENT=30 -ON=31 -POLICY=32 -DEFAULT=33 -SOURCE=34 -REPOSITORY=35 -COMMIT=36 -REVISION=37 -SEMANTIC_MAJOR=38 -ON_DELETE=39 -RETAIN_OTHER=40 -KEYED=41 -PUBLIC_TRAVERSAL=42 -ID=43 -DOC=44 -MODE=45 -EMITS=46 -RECEIVER=47 -REQUIRES=48 -ANY=49 -GET=50 -SET=51 -WATCH=52 -START=53 -STOP=54 -READ=55 -WRITE=56 -RESOLVE=57 -CONNECT=58 -DISCONNECT=59 -CALL=60 -WATCH_START=61 -WATCH_STOP=62 -SUBSCRIBE=63 -UNSUBSCRIBE=64 -OPTIMISTIC_REGISTER=65 -CRDT=66 -OPTIONAL_ONE=67 -EXACTLY_ONE=68 -MANY_UNIQUE=69 -MANY=70 -ORDERED=71 -UNIT=72 -WATCH_HANDLE=73 -MESSAGE=74 -ATOM_REF=75 -INTERFACE_REF=76 -OPTIONAL=77 -LIST=78 -RECORD=79 -BOOL=80 -BYTES=81 -DOUBLE=82 -INT32=83 -INT64=84 -STRING=85 -UINT32=86 -UINT64=87 -TRUE=88 -FALSE=89 -NULL=90 -ARROW=91 -COLON=92 -SEMI=93 -COMMA=94 -DOT=95 -LBRACE=96 -RBRACE=97 -LBRACK=98 -RBRACK=99 -LPAREN=100 -RPAREN=101 -LT=102 -GT=103 -INTEGER=104 -JSON_NUMBER=105 -IDENTIFIER=106 -STRING_LITERAL=107 -LINE_COMMENT=108 -BLOCK_COMMENT=109 -WS=110 +TYPE=2 +OBJECT=3 +STORABLE=4 +IMPLEMENTS=5 +REF=6 +FRAGMENT=7 +IMPORT=8 +EXTERNAL=9 +ATOM=10 +INTERFACE=11 +INTERFACES=12 +PACKAGE=13 +VALUE=14 +RELATION=15 +OPERATION=16 +FUNCTION=17 +CONSTRUCTOR=18 +CONSTRUCTS=19 +INPUT=20 +CONFORM=21 +AS=22 +BIND=23 +TO=24 +PRIVATE=25 +SHARED=26 +STATE=27 +EDGE=28 +PROJECTION=29 +WITH=30 +USING=31 +VIA=32 +MATERIALIZE=33 +IF=34 +ABSENT=35 +ON=36 +POLICY=37 +DEFAULT=38 +SOURCE=39 +REPOSITORY=40 +COMMIT=41 +REVISION=42 +SEMANTIC_MAJOR=43 +ON_DELETE=44 +RETAIN_OTHER=45 +KEYED=46 +PUBLIC_TRAVERSAL=47 +ID=48 +DOC=49 +MODE=50 +EMITS=51 +RECEIVER=52 +REQUIRES=53 +ANY=54 +GET=55 +SET=56 +WATCH=57 +START=58 +STOP=59 +READ=60 +WRITE=61 +RESOLVE=62 +CONNECT=63 +DISCONNECT=64 +CALL=65 +WATCH_START=66 +WATCH_STOP=67 +SUBSCRIBE=68 +UNSUBSCRIBE=69 +OPTIMISTIC_REGISTER=70 +CRDT=71 +OPTIONAL_ONE=72 +EXACTLY_ONE=73 +MANY_UNIQUE=74 +MANY=75 +ORDERED=76 +UNIT=77 +WATCH_HANDLE=78 +MESSAGE=79 +ATOM_REF=80 +INTERFACE_REF=81 +OPTIONAL=82 +LIST=83 +RECORD=84 +BOOL=85 +BYTES=86 +DOUBLE=87 +INT32=88 +INT64=89 +STRING=90 +UINT32=91 +UINT64=92 +TRUE=93 +FALSE=94 +NULL=95 +ARROW=96 +COLON=97 +SEMI=98 +COMMA=99 +DOT=100 +LBRACE=101 +RBRACE=102 +LBRACK=103 +RBRACK=104 +LPAREN=105 +RPAREN=106 +LT=107 +GT=108 +AMP=109 +EQUAL=110 +INTEGER=111 +JSON_NUMBER=112 +IDENTIFIER=113 +STRING_LITERAL=114 +LINE_COMMENT=115 +BLOCK_COMMENT=116 +WS=117 'workspace'=1 -'fragment'=2 -'import'=3 -'external'=4 -'atom'=5 -'interface'=6 -'interfaces'=7 -'package'=8 -'value'=9 -'relation'=10 -'operation'=11 -'function'=12 -'constructor'=13 -'constructs'=14 -'input'=15 -'conform'=16 -'as'=17 -'bind'=18 -'to'=19 -'private'=20 -'shared'=21 -'state'=22 -'edge'=23 -'projection'=24 -'with'=25 -'using'=26 -'via'=27 -'materialize'=28 -'if'=29 -'absent'=30 -'on'=31 -'policy'=32 -'default'=33 -'source'=34 -'repository'=35 -'commit'=36 -'revision'=37 -'semantic-major'=38 -'on-delete'=39 -'retain-other'=40 -'keyed'=41 -'public-traversal'=42 -'id'=43 -'doc'=44 -'mode'=45 -'emits'=46 -'receiver'=47 -'requires'=48 -'any'=49 -'get'=50 -'set'=51 -'watch'=52 -'start'=53 -'stop'=54 -'read'=55 -'write'=56 -'resolve'=57 -'connect'=58 -'disconnect'=59 -'call'=60 -'watch-start'=61 -'watch-stop'=62 -'subscribe'=63 -'unsubscribe'=64 -'optimistic-register'=65 -'crdt'=66 -'optional-one'=67 -'exactly-one'=68 -'many-unique'=69 -'many'=70 -'ordered'=71 -'unit'=72 -'watch-handle'=73 -'message'=74 -'atom-ref'=75 -'interface-ref'=76 -'optional'=77 -'list'=78 -'record'=79 -'bool'=80 -'bytes'=81 -'double'=82 -'int32'=83 -'int64'=84 -'string'=85 -'uint32'=86 -'uint64'=87 -'true'=88 -'false'=89 -'null'=90 -'->'=91 -':'=92 -';'=93 -','=94 -'.'=95 -'{'=96 -'}'=97 -'['=98 -']'=99 -'('=100 -')'=101 -'<'=102 -'>'=103 +'type'=2 +'object'=3 +'storable'=4 +'implements'=5 +'ref'=6 +'fragment'=7 +'import'=8 +'external'=9 +'atom'=10 +'interface'=11 +'interfaces'=12 +'package'=13 +'value'=14 +'relation'=15 +'operation'=16 +'function'=17 +'constructor'=18 +'constructs'=19 +'input'=20 +'conform'=21 +'as'=22 +'bind'=23 +'to'=24 +'private'=25 +'shared'=26 +'state'=27 +'edge'=28 +'projection'=29 +'with'=30 +'using'=31 +'via'=32 +'materialize'=33 +'if'=34 +'absent'=35 +'on'=36 +'policy'=37 +'default'=38 +'source'=39 +'repository'=40 +'commit'=41 +'revision'=42 +'semantic-major'=43 +'on-delete'=44 +'retain-other'=45 +'keyed'=46 +'public-traversal'=47 +'id'=48 +'doc'=49 +'mode'=50 +'emits'=51 +'receiver'=52 +'requires'=53 +'any'=54 +'get'=55 +'set'=56 +'watch'=57 +'start'=58 +'stop'=59 +'read'=60 +'write'=61 +'resolve'=62 +'connect'=63 +'disconnect'=64 +'call'=65 +'watch-start'=66 +'watch-stop'=67 +'subscribe'=68 +'unsubscribe'=69 +'optimistic-register'=70 +'crdt'=71 +'optional-one'=72 +'exactly-one'=73 +'many-unique'=74 +'many'=75 +'ordered'=76 +'unit'=77 +'watch-handle'=78 +'message'=79 +'atom-ref'=80 +'interface-ref'=81 +'optional'=82 +'list'=83 +'record'=84 +'bool'=85 +'bytes'=86 +'double'=87 +'int32'=88 +'int64'=89 +'string'=90 +'uint32'=91 +'uint64'=92 +'true'=93 +'false'=94 +'null'=95 +'->'=96 +':'=97 +';'=98 +','=99 +'.'=100 +'{'=101 +'}'=102 +'['=103 +']'=104 +'('=105 +')'=106 +'<'=107 +'>'=108 +'&'=109 +'='=110 diff --git a/src/capability-language/generated/QuixosCapabilityLexer.interp b/src/capability-language/generated/QuixosCapabilityLexer.interp index 094257a..4e55c58 100644 --- a/src/capability-language/generated/QuixosCapabilityLexer.interp +++ b/src/capability-language/generated/QuixosCapabilityLexer.interp @@ -1,6 +1,11 @@ token literal names: null 'workspace' +'type' +'object' +'storable' +'implements' +'ref' 'fragment' 'import' 'external' @@ -103,6 +108,8 @@ null ')' '<' '>' +'&' +'=' null null null @@ -114,6 +121,11 @@ null token symbolic names: null WORKSPACE +TYPE +OBJECT +STORABLE +IMPLEMENTS +REF FRAGMENT IMPORT EXTERNAL @@ -216,6 +228,8 @@ LPAREN RPAREN LT GT +AMP +EQUAL INTEGER JSON_NUMBER IDENTIFIER @@ -226,6 +240,11 @@ WS rule names: WORKSPACE +TYPE +OBJECT +STORABLE +IMPLEMENTS +REF FRAGMENT IMPORT EXTERNAL @@ -328,6 +347,8 @@ LPAREN RPAREN LT GT +AMP +EQUAL INTEGER JSON_NUMBER IDENTIFIER @@ -346,4 +367,4 @@ mode names: DEFAULT_MODE atn: -[4, 0, 110, 1056, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 2, 88, 7, 88, 2, 89, 7, 89, 2, 90, 7, 90, 2, 91, 7, 91, 2, 92, 7, 92, 2, 93, 7, 93, 2, 94, 7, 94, 2, 95, 7, 95, 2, 96, 7, 96, 2, 97, 7, 97, 2, 98, 7, 98, 2, 99, 7, 99, 2, 100, 7, 100, 2, 101, 7, 101, 2, 102, 7, 102, 2, 103, 7, 103, 2, 104, 7, 104, 2, 105, 7, 105, 2, 106, 7, 106, 2, 107, 7, 107, 2, 108, 7, 108, 2, 109, 7, 109, 2, 110, 7, 110, 2, 111, 7, 111, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 83, 1, 83, 1, 83, 1, 83, 1, 83, 1, 83, 1, 84, 1, 84, 1, 84, 1, 84, 1, 84, 1, 84, 1, 84, 1, 85, 1, 85, 1, 85, 1, 85, 1, 85, 1, 85, 1, 85, 1, 86, 1, 86, 1, 86, 1, 86, 1, 86, 1, 86, 1, 86, 1, 87, 1, 87, 1, 87, 1, 87, 1, 87, 1, 88, 1, 88, 1, 88, 1, 88, 1, 88, 1, 88, 1, 89, 1, 89, 1, 89, 1, 89, 1, 89, 1, 90, 1, 90, 1, 90, 1, 91, 1, 91, 1, 92, 1, 92, 1, 93, 1, 93, 1, 94, 1, 94, 1, 95, 1, 95, 1, 96, 1, 96, 1, 97, 1, 97, 1, 98, 1, 98, 1, 99, 1, 99, 1, 100, 1, 100, 1, 101, 1, 101, 1, 102, 1, 102, 1, 103, 3, 103, 957, 8, 103, 1, 103, 4, 103, 960, 8, 103, 11, 103, 12, 103, 961, 1, 104, 3, 104, 965, 8, 104, 1, 104, 1, 104, 1, 104, 5, 104, 970, 8, 104, 10, 104, 12, 104, 973, 9, 104, 3, 104, 975, 8, 104, 1, 104, 1, 104, 4, 104, 979, 8, 104, 11, 104, 12, 104, 980, 3, 104, 983, 8, 104, 1, 104, 1, 104, 3, 104, 987, 8, 104, 1, 104, 4, 104, 990, 8, 104, 11, 104, 12, 104, 991, 3, 104, 994, 8, 104, 1, 105, 1, 105, 5, 105, 998, 8, 105, 10, 105, 12, 105, 1001, 9, 105, 1, 106, 1, 106, 1, 106, 5, 106, 1006, 8, 106, 10, 106, 12, 106, 1009, 9, 106, 1, 106, 1, 106, 1, 107, 1, 107, 1, 107, 1, 107, 1, 107, 1, 107, 1, 107, 1, 107, 3, 107, 1021, 8, 107, 1, 108, 1, 108, 1, 109, 1, 109, 1, 109, 1, 109, 5, 109, 1029, 8, 109, 10, 109, 12, 109, 1032, 9, 109, 1, 109, 1, 109, 1, 110, 1, 110, 1, 110, 1, 110, 5, 110, 1040, 8, 110, 10, 110, 12, 110, 1043, 9, 110, 1, 110, 1, 110, 1, 110, 1, 110, 1, 110, 1, 111, 4, 111, 1051, 8, 111, 11, 111, 12, 111, 1052, 1, 111, 1, 111, 1, 1041, 0, 112, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 33, 67, 34, 69, 35, 71, 36, 73, 37, 75, 38, 77, 39, 79, 40, 81, 41, 83, 42, 85, 43, 87, 44, 89, 45, 91, 46, 93, 47, 95, 48, 97, 49, 99, 50, 101, 51, 103, 52, 105, 53, 107, 54, 109, 55, 111, 56, 113, 57, 115, 58, 117, 59, 119, 60, 121, 61, 123, 62, 125, 63, 127, 64, 129, 65, 131, 66, 133, 67, 135, 68, 137, 69, 139, 70, 141, 71, 143, 72, 145, 73, 147, 74, 149, 75, 151, 76, 153, 77, 155, 78, 157, 79, 159, 80, 161, 81, 163, 82, 165, 83, 167, 84, 169, 85, 171, 86, 173, 87, 175, 88, 177, 89, 179, 90, 181, 91, 183, 92, 185, 93, 187, 94, 189, 95, 191, 96, 193, 97, 195, 98, 197, 99, 199, 100, 201, 101, 203, 102, 205, 103, 207, 104, 209, 105, 211, 106, 213, 107, 215, 0, 217, 0, 219, 108, 221, 109, 223, 110, 1, 0, 11, 1, 0, 48, 57, 1, 0, 49, 57, 2, 0, 69, 69, 101, 101, 2, 0, 43, 43, 45, 45, 3, 0, 65, 90, 95, 95, 97, 122, 4, 0, 48, 57, 65, 90, 95, 95, 97, 122, 4, 0, 10, 10, 13, 13, 34, 34, 92, 92, 8, 0, 34, 34, 47, 47, 92, 92, 98, 98, 102, 102, 110, 110, 114, 114, 116, 116, 3, 0, 48, 57, 65, 70, 97, 102, 2, 0, 10, 10, 13, 13, 3, 0, 9, 10, 13, 13, 32, 32, 1070, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0, 65, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 73, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, 81, 1, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0, 89, 1, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 93, 1, 0, 0, 0, 0, 95, 1, 0, 0, 0, 0, 97, 1, 0, 0, 0, 0, 99, 1, 0, 0, 0, 0, 101, 1, 0, 0, 0, 0, 103, 1, 0, 0, 0, 0, 105, 1, 0, 0, 0, 0, 107, 1, 0, 0, 0, 0, 109, 1, 0, 0, 0, 0, 111, 1, 0, 0, 0, 0, 113, 1, 0, 0, 0, 0, 115, 1, 0, 0, 0, 0, 117, 1, 0, 0, 0, 0, 119, 1, 0, 0, 0, 0, 121, 1, 0, 0, 0, 0, 123, 1, 0, 0, 0, 0, 125, 1, 0, 0, 0, 0, 127, 1, 0, 0, 0, 0, 129, 1, 0, 0, 0, 0, 131, 1, 0, 0, 0, 0, 133, 1, 0, 0, 0, 0, 135, 1, 0, 0, 0, 0, 137, 1, 0, 0, 0, 0, 139, 1, 0, 0, 0, 0, 141, 1, 0, 0, 0, 0, 143, 1, 0, 0, 0, 0, 145, 1, 0, 0, 0, 0, 147, 1, 0, 0, 0, 0, 149, 1, 0, 0, 0, 0, 151, 1, 0, 0, 0, 0, 153, 1, 0, 0, 0, 0, 155, 1, 0, 0, 0, 0, 157, 1, 0, 0, 0, 0, 159, 1, 0, 0, 0, 0, 161, 1, 0, 0, 0, 0, 163, 1, 0, 0, 0, 0, 165, 1, 0, 0, 0, 0, 167, 1, 0, 0, 0, 0, 169, 1, 0, 0, 0, 0, 171, 1, 0, 0, 0, 0, 173, 1, 0, 0, 0, 0, 175, 1, 0, 0, 0, 0, 177, 1, 0, 0, 0, 0, 179, 1, 0, 0, 0, 0, 181, 1, 0, 0, 0, 0, 183, 1, 0, 0, 0, 0, 185, 1, 0, 0, 0, 0, 187, 1, 0, 0, 0, 0, 189, 1, 0, 0, 0, 0, 191, 1, 0, 0, 0, 0, 193, 1, 0, 0, 0, 0, 195, 1, 0, 0, 0, 0, 197, 1, 0, 0, 0, 0, 199, 1, 0, 0, 0, 0, 201, 1, 0, 0, 0, 0, 203, 1, 0, 0, 0, 0, 205, 1, 0, 0, 0, 0, 207, 1, 0, 0, 0, 0, 209, 1, 0, 0, 0, 0, 211, 1, 0, 0, 0, 0, 213, 1, 0, 0, 0, 0, 219, 1, 0, 0, 0, 0, 221, 1, 0, 0, 0, 0, 223, 1, 0, 0, 0, 1, 225, 1, 0, 0, 0, 3, 235, 1, 0, 0, 0, 5, 244, 1, 0, 0, 0, 7, 251, 1, 0, 0, 0, 9, 260, 1, 0, 0, 0, 11, 265, 1, 0, 0, 0, 13, 275, 1, 0, 0, 0, 15, 286, 1, 0, 0, 0, 17, 294, 1, 0, 0, 0, 19, 300, 1, 0, 0, 0, 21, 309, 1, 0, 0, 0, 23, 319, 1, 0, 0, 0, 25, 328, 1, 0, 0, 0, 27, 340, 1, 0, 0, 0, 29, 351, 1, 0, 0, 0, 31, 357, 1, 0, 0, 0, 33, 365, 1, 0, 0, 0, 35, 368, 1, 0, 0, 0, 37, 373, 1, 0, 0, 0, 39, 376, 1, 0, 0, 0, 41, 384, 1, 0, 0, 0, 43, 391, 1, 0, 0, 0, 45, 397, 1, 0, 0, 0, 47, 402, 1, 0, 0, 0, 49, 413, 1, 0, 0, 0, 51, 418, 1, 0, 0, 0, 53, 424, 1, 0, 0, 0, 55, 428, 1, 0, 0, 0, 57, 440, 1, 0, 0, 0, 59, 443, 1, 0, 0, 0, 61, 450, 1, 0, 0, 0, 63, 453, 1, 0, 0, 0, 65, 460, 1, 0, 0, 0, 67, 468, 1, 0, 0, 0, 69, 475, 1, 0, 0, 0, 71, 486, 1, 0, 0, 0, 73, 493, 1, 0, 0, 0, 75, 502, 1, 0, 0, 0, 77, 517, 1, 0, 0, 0, 79, 527, 1, 0, 0, 0, 81, 540, 1, 0, 0, 0, 83, 546, 1, 0, 0, 0, 85, 563, 1, 0, 0, 0, 87, 566, 1, 0, 0, 0, 89, 570, 1, 0, 0, 0, 91, 575, 1, 0, 0, 0, 93, 581, 1, 0, 0, 0, 95, 590, 1, 0, 0, 0, 97, 599, 1, 0, 0, 0, 99, 603, 1, 0, 0, 0, 101, 607, 1, 0, 0, 0, 103, 611, 1, 0, 0, 0, 105, 617, 1, 0, 0, 0, 107, 623, 1, 0, 0, 0, 109, 628, 1, 0, 0, 0, 111, 633, 1, 0, 0, 0, 113, 639, 1, 0, 0, 0, 115, 647, 1, 0, 0, 0, 117, 655, 1, 0, 0, 0, 119, 666, 1, 0, 0, 0, 121, 671, 1, 0, 0, 0, 123, 683, 1, 0, 0, 0, 125, 694, 1, 0, 0, 0, 127, 704, 1, 0, 0, 0, 129, 716, 1, 0, 0, 0, 131, 736, 1, 0, 0, 0, 133, 741, 1, 0, 0, 0, 135, 754, 1, 0, 0, 0, 137, 766, 1, 0, 0, 0, 139, 778, 1, 0, 0, 0, 141, 783, 1, 0, 0, 0, 143, 791, 1, 0, 0, 0, 145, 796, 1, 0, 0, 0, 147, 809, 1, 0, 0, 0, 149, 817, 1, 0, 0, 0, 151, 826, 1, 0, 0, 0, 153, 840, 1, 0, 0, 0, 155, 849, 1, 0, 0, 0, 157, 854, 1, 0, 0, 0, 159, 861, 1, 0, 0, 0, 161, 866, 1, 0, 0, 0, 163, 872, 1, 0, 0, 0, 165, 879, 1, 0, 0, 0, 167, 885, 1, 0, 0, 0, 169, 891, 1, 0, 0, 0, 171, 898, 1, 0, 0, 0, 173, 905, 1, 0, 0, 0, 175, 912, 1, 0, 0, 0, 177, 917, 1, 0, 0, 0, 179, 923, 1, 0, 0, 0, 181, 928, 1, 0, 0, 0, 183, 931, 1, 0, 0, 0, 185, 933, 1, 0, 0, 0, 187, 935, 1, 0, 0, 0, 189, 937, 1, 0, 0, 0, 191, 939, 1, 0, 0, 0, 193, 941, 1, 0, 0, 0, 195, 943, 1, 0, 0, 0, 197, 945, 1, 0, 0, 0, 199, 947, 1, 0, 0, 0, 201, 949, 1, 0, 0, 0, 203, 951, 1, 0, 0, 0, 205, 953, 1, 0, 0, 0, 207, 956, 1, 0, 0, 0, 209, 964, 1, 0, 0, 0, 211, 995, 1, 0, 0, 0, 213, 1002, 1, 0, 0, 0, 215, 1012, 1, 0, 0, 0, 217, 1022, 1, 0, 0, 0, 219, 1024, 1, 0, 0, 0, 221, 1035, 1, 0, 0, 0, 223, 1050, 1, 0, 0, 0, 225, 226, 5, 119, 0, 0, 226, 227, 5, 111, 0, 0, 227, 228, 5, 114, 0, 0, 228, 229, 5, 107, 0, 0, 229, 230, 5, 115, 0, 0, 230, 231, 5, 112, 0, 0, 231, 232, 5, 97, 0, 0, 232, 233, 5, 99, 0, 0, 233, 234, 5, 101, 0, 0, 234, 2, 1, 0, 0, 0, 235, 236, 5, 102, 0, 0, 236, 237, 5, 114, 0, 0, 237, 238, 5, 97, 0, 0, 238, 239, 5, 103, 0, 0, 239, 240, 5, 109, 0, 0, 240, 241, 5, 101, 0, 0, 241, 242, 5, 110, 0, 0, 242, 243, 5, 116, 0, 0, 243, 4, 1, 0, 0, 0, 244, 245, 5, 105, 0, 0, 245, 246, 5, 109, 0, 0, 246, 247, 5, 112, 0, 0, 247, 248, 5, 111, 0, 0, 248, 249, 5, 114, 0, 0, 249, 250, 5, 116, 0, 0, 250, 6, 1, 0, 0, 0, 251, 252, 5, 101, 0, 0, 252, 253, 5, 120, 0, 0, 253, 254, 5, 116, 0, 0, 254, 255, 5, 101, 0, 0, 255, 256, 5, 114, 0, 0, 256, 257, 5, 110, 0, 0, 257, 258, 5, 97, 0, 0, 258, 259, 5, 108, 0, 0, 259, 8, 1, 0, 0, 0, 260, 261, 5, 97, 0, 0, 261, 262, 5, 116, 0, 0, 262, 263, 5, 111, 0, 0, 263, 264, 5, 109, 0, 0, 264, 10, 1, 0, 0, 0, 265, 266, 5, 105, 0, 0, 266, 267, 5, 110, 0, 0, 267, 268, 5, 116, 0, 0, 268, 269, 5, 101, 0, 0, 269, 270, 5, 114, 0, 0, 270, 271, 5, 102, 0, 0, 271, 272, 5, 97, 0, 0, 272, 273, 5, 99, 0, 0, 273, 274, 5, 101, 0, 0, 274, 12, 1, 0, 0, 0, 275, 276, 5, 105, 0, 0, 276, 277, 5, 110, 0, 0, 277, 278, 5, 116, 0, 0, 278, 279, 5, 101, 0, 0, 279, 280, 5, 114, 0, 0, 280, 281, 5, 102, 0, 0, 281, 282, 5, 97, 0, 0, 282, 283, 5, 99, 0, 0, 283, 284, 5, 101, 0, 0, 284, 285, 5, 115, 0, 0, 285, 14, 1, 0, 0, 0, 286, 287, 5, 112, 0, 0, 287, 288, 5, 97, 0, 0, 288, 289, 5, 99, 0, 0, 289, 290, 5, 107, 0, 0, 290, 291, 5, 97, 0, 0, 291, 292, 5, 103, 0, 0, 292, 293, 5, 101, 0, 0, 293, 16, 1, 0, 0, 0, 294, 295, 5, 118, 0, 0, 295, 296, 5, 97, 0, 0, 296, 297, 5, 108, 0, 0, 297, 298, 5, 117, 0, 0, 298, 299, 5, 101, 0, 0, 299, 18, 1, 0, 0, 0, 300, 301, 5, 114, 0, 0, 301, 302, 5, 101, 0, 0, 302, 303, 5, 108, 0, 0, 303, 304, 5, 97, 0, 0, 304, 305, 5, 116, 0, 0, 305, 306, 5, 105, 0, 0, 306, 307, 5, 111, 0, 0, 307, 308, 5, 110, 0, 0, 308, 20, 1, 0, 0, 0, 309, 310, 5, 111, 0, 0, 310, 311, 5, 112, 0, 0, 311, 312, 5, 101, 0, 0, 312, 313, 5, 114, 0, 0, 313, 314, 5, 97, 0, 0, 314, 315, 5, 116, 0, 0, 315, 316, 5, 105, 0, 0, 316, 317, 5, 111, 0, 0, 317, 318, 5, 110, 0, 0, 318, 22, 1, 0, 0, 0, 319, 320, 5, 102, 0, 0, 320, 321, 5, 117, 0, 0, 321, 322, 5, 110, 0, 0, 322, 323, 5, 99, 0, 0, 323, 324, 5, 116, 0, 0, 324, 325, 5, 105, 0, 0, 325, 326, 5, 111, 0, 0, 326, 327, 5, 110, 0, 0, 327, 24, 1, 0, 0, 0, 328, 329, 5, 99, 0, 0, 329, 330, 5, 111, 0, 0, 330, 331, 5, 110, 0, 0, 331, 332, 5, 115, 0, 0, 332, 333, 5, 116, 0, 0, 333, 334, 5, 114, 0, 0, 334, 335, 5, 117, 0, 0, 335, 336, 5, 99, 0, 0, 336, 337, 5, 116, 0, 0, 337, 338, 5, 111, 0, 0, 338, 339, 5, 114, 0, 0, 339, 26, 1, 0, 0, 0, 340, 341, 5, 99, 0, 0, 341, 342, 5, 111, 0, 0, 342, 343, 5, 110, 0, 0, 343, 344, 5, 115, 0, 0, 344, 345, 5, 116, 0, 0, 345, 346, 5, 114, 0, 0, 346, 347, 5, 117, 0, 0, 347, 348, 5, 99, 0, 0, 348, 349, 5, 116, 0, 0, 349, 350, 5, 115, 0, 0, 350, 28, 1, 0, 0, 0, 351, 352, 5, 105, 0, 0, 352, 353, 5, 110, 0, 0, 353, 354, 5, 112, 0, 0, 354, 355, 5, 117, 0, 0, 355, 356, 5, 116, 0, 0, 356, 30, 1, 0, 0, 0, 357, 358, 5, 99, 0, 0, 358, 359, 5, 111, 0, 0, 359, 360, 5, 110, 0, 0, 360, 361, 5, 102, 0, 0, 361, 362, 5, 111, 0, 0, 362, 363, 5, 114, 0, 0, 363, 364, 5, 109, 0, 0, 364, 32, 1, 0, 0, 0, 365, 366, 5, 97, 0, 0, 366, 367, 5, 115, 0, 0, 367, 34, 1, 0, 0, 0, 368, 369, 5, 98, 0, 0, 369, 370, 5, 105, 0, 0, 370, 371, 5, 110, 0, 0, 371, 372, 5, 100, 0, 0, 372, 36, 1, 0, 0, 0, 373, 374, 5, 116, 0, 0, 374, 375, 5, 111, 0, 0, 375, 38, 1, 0, 0, 0, 376, 377, 5, 112, 0, 0, 377, 378, 5, 114, 0, 0, 378, 379, 5, 105, 0, 0, 379, 380, 5, 118, 0, 0, 380, 381, 5, 97, 0, 0, 381, 382, 5, 116, 0, 0, 382, 383, 5, 101, 0, 0, 383, 40, 1, 0, 0, 0, 384, 385, 5, 115, 0, 0, 385, 386, 5, 104, 0, 0, 386, 387, 5, 97, 0, 0, 387, 388, 5, 114, 0, 0, 388, 389, 5, 101, 0, 0, 389, 390, 5, 100, 0, 0, 390, 42, 1, 0, 0, 0, 391, 392, 5, 115, 0, 0, 392, 393, 5, 116, 0, 0, 393, 394, 5, 97, 0, 0, 394, 395, 5, 116, 0, 0, 395, 396, 5, 101, 0, 0, 396, 44, 1, 0, 0, 0, 397, 398, 5, 101, 0, 0, 398, 399, 5, 100, 0, 0, 399, 400, 5, 103, 0, 0, 400, 401, 5, 101, 0, 0, 401, 46, 1, 0, 0, 0, 402, 403, 5, 112, 0, 0, 403, 404, 5, 114, 0, 0, 404, 405, 5, 111, 0, 0, 405, 406, 5, 106, 0, 0, 406, 407, 5, 101, 0, 0, 407, 408, 5, 99, 0, 0, 408, 409, 5, 116, 0, 0, 409, 410, 5, 105, 0, 0, 410, 411, 5, 111, 0, 0, 411, 412, 5, 110, 0, 0, 412, 48, 1, 0, 0, 0, 413, 414, 5, 119, 0, 0, 414, 415, 5, 105, 0, 0, 415, 416, 5, 116, 0, 0, 416, 417, 5, 104, 0, 0, 417, 50, 1, 0, 0, 0, 418, 419, 5, 117, 0, 0, 419, 420, 5, 115, 0, 0, 420, 421, 5, 105, 0, 0, 421, 422, 5, 110, 0, 0, 422, 423, 5, 103, 0, 0, 423, 52, 1, 0, 0, 0, 424, 425, 5, 118, 0, 0, 425, 426, 5, 105, 0, 0, 426, 427, 5, 97, 0, 0, 427, 54, 1, 0, 0, 0, 428, 429, 5, 109, 0, 0, 429, 430, 5, 97, 0, 0, 430, 431, 5, 116, 0, 0, 431, 432, 5, 101, 0, 0, 432, 433, 5, 114, 0, 0, 433, 434, 5, 105, 0, 0, 434, 435, 5, 97, 0, 0, 435, 436, 5, 108, 0, 0, 436, 437, 5, 105, 0, 0, 437, 438, 5, 122, 0, 0, 438, 439, 5, 101, 0, 0, 439, 56, 1, 0, 0, 0, 440, 441, 5, 105, 0, 0, 441, 442, 5, 102, 0, 0, 442, 58, 1, 0, 0, 0, 443, 444, 5, 97, 0, 0, 444, 445, 5, 98, 0, 0, 445, 446, 5, 115, 0, 0, 446, 447, 5, 101, 0, 0, 447, 448, 5, 110, 0, 0, 448, 449, 5, 116, 0, 0, 449, 60, 1, 0, 0, 0, 450, 451, 5, 111, 0, 0, 451, 452, 5, 110, 0, 0, 452, 62, 1, 0, 0, 0, 453, 454, 5, 112, 0, 0, 454, 455, 5, 111, 0, 0, 455, 456, 5, 108, 0, 0, 456, 457, 5, 105, 0, 0, 457, 458, 5, 99, 0, 0, 458, 459, 5, 121, 0, 0, 459, 64, 1, 0, 0, 0, 460, 461, 5, 100, 0, 0, 461, 462, 5, 101, 0, 0, 462, 463, 5, 102, 0, 0, 463, 464, 5, 97, 0, 0, 464, 465, 5, 117, 0, 0, 465, 466, 5, 108, 0, 0, 466, 467, 5, 116, 0, 0, 467, 66, 1, 0, 0, 0, 468, 469, 5, 115, 0, 0, 469, 470, 5, 111, 0, 0, 470, 471, 5, 117, 0, 0, 471, 472, 5, 114, 0, 0, 472, 473, 5, 99, 0, 0, 473, 474, 5, 101, 0, 0, 474, 68, 1, 0, 0, 0, 475, 476, 5, 114, 0, 0, 476, 477, 5, 101, 0, 0, 477, 478, 5, 112, 0, 0, 478, 479, 5, 111, 0, 0, 479, 480, 5, 115, 0, 0, 480, 481, 5, 105, 0, 0, 481, 482, 5, 116, 0, 0, 482, 483, 5, 111, 0, 0, 483, 484, 5, 114, 0, 0, 484, 485, 5, 121, 0, 0, 485, 70, 1, 0, 0, 0, 486, 487, 5, 99, 0, 0, 487, 488, 5, 111, 0, 0, 488, 489, 5, 109, 0, 0, 489, 490, 5, 109, 0, 0, 490, 491, 5, 105, 0, 0, 491, 492, 5, 116, 0, 0, 492, 72, 1, 0, 0, 0, 493, 494, 5, 114, 0, 0, 494, 495, 5, 101, 0, 0, 495, 496, 5, 118, 0, 0, 496, 497, 5, 105, 0, 0, 497, 498, 5, 115, 0, 0, 498, 499, 5, 105, 0, 0, 499, 500, 5, 111, 0, 0, 500, 501, 5, 110, 0, 0, 501, 74, 1, 0, 0, 0, 502, 503, 5, 115, 0, 0, 503, 504, 5, 101, 0, 0, 504, 505, 5, 109, 0, 0, 505, 506, 5, 97, 0, 0, 506, 507, 5, 110, 0, 0, 507, 508, 5, 116, 0, 0, 508, 509, 5, 105, 0, 0, 509, 510, 5, 99, 0, 0, 510, 511, 5, 45, 0, 0, 511, 512, 5, 109, 0, 0, 512, 513, 5, 97, 0, 0, 513, 514, 5, 106, 0, 0, 514, 515, 5, 111, 0, 0, 515, 516, 5, 114, 0, 0, 516, 76, 1, 0, 0, 0, 517, 518, 5, 111, 0, 0, 518, 519, 5, 110, 0, 0, 519, 520, 5, 45, 0, 0, 520, 521, 5, 100, 0, 0, 521, 522, 5, 101, 0, 0, 522, 523, 5, 108, 0, 0, 523, 524, 5, 101, 0, 0, 524, 525, 5, 116, 0, 0, 525, 526, 5, 101, 0, 0, 526, 78, 1, 0, 0, 0, 527, 528, 5, 114, 0, 0, 528, 529, 5, 101, 0, 0, 529, 530, 5, 116, 0, 0, 530, 531, 5, 97, 0, 0, 531, 532, 5, 105, 0, 0, 532, 533, 5, 110, 0, 0, 533, 534, 5, 45, 0, 0, 534, 535, 5, 111, 0, 0, 535, 536, 5, 116, 0, 0, 536, 537, 5, 104, 0, 0, 537, 538, 5, 101, 0, 0, 538, 539, 5, 114, 0, 0, 539, 80, 1, 0, 0, 0, 540, 541, 5, 107, 0, 0, 541, 542, 5, 101, 0, 0, 542, 543, 5, 121, 0, 0, 543, 544, 5, 101, 0, 0, 544, 545, 5, 100, 0, 0, 545, 82, 1, 0, 0, 0, 546, 547, 5, 112, 0, 0, 547, 548, 5, 117, 0, 0, 548, 549, 5, 98, 0, 0, 549, 550, 5, 108, 0, 0, 550, 551, 5, 105, 0, 0, 551, 552, 5, 99, 0, 0, 552, 553, 5, 45, 0, 0, 553, 554, 5, 116, 0, 0, 554, 555, 5, 114, 0, 0, 555, 556, 5, 97, 0, 0, 556, 557, 5, 118, 0, 0, 557, 558, 5, 101, 0, 0, 558, 559, 5, 114, 0, 0, 559, 560, 5, 115, 0, 0, 560, 561, 5, 97, 0, 0, 561, 562, 5, 108, 0, 0, 562, 84, 1, 0, 0, 0, 563, 564, 5, 105, 0, 0, 564, 565, 5, 100, 0, 0, 565, 86, 1, 0, 0, 0, 566, 567, 5, 100, 0, 0, 567, 568, 5, 111, 0, 0, 568, 569, 5, 99, 0, 0, 569, 88, 1, 0, 0, 0, 570, 571, 5, 109, 0, 0, 571, 572, 5, 111, 0, 0, 572, 573, 5, 100, 0, 0, 573, 574, 5, 101, 0, 0, 574, 90, 1, 0, 0, 0, 575, 576, 5, 101, 0, 0, 576, 577, 5, 109, 0, 0, 577, 578, 5, 105, 0, 0, 578, 579, 5, 116, 0, 0, 579, 580, 5, 115, 0, 0, 580, 92, 1, 0, 0, 0, 581, 582, 5, 114, 0, 0, 582, 583, 5, 101, 0, 0, 583, 584, 5, 99, 0, 0, 584, 585, 5, 101, 0, 0, 585, 586, 5, 105, 0, 0, 586, 587, 5, 118, 0, 0, 587, 588, 5, 101, 0, 0, 588, 589, 5, 114, 0, 0, 589, 94, 1, 0, 0, 0, 590, 591, 5, 114, 0, 0, 591, 592, 5, 101, 0, 0, 592, 593, 5, 113, 0, 0, 593, 594, 5, 117, 0, 0, 594, 595, 5, 105, 0, 0, 595, 596, 5, 114, 0, 0, 596, 597, 5, 101, 0, 0, 597, 598, 5, 115, 0, 0, 598, 96, 1, 0, 0, 0, 599, 600, 5, 97, 0, 0, 600, 601, 5, 110, 0, 0, 601, 602, 5, 121, 0, 0, 602, 98, 1, 0, 0, 0, 603, 604, 5, 103, 0, 0, 604, 605, 5, 101, 0, 0, 605, 606, 5, 116, 0, 0, 606, 100, 1, 0, 0, 0, 607, 608, 5, 115, 0, 0, 608, 609, 5, 101, 0, 0, 609, 610, 5, 116, 0, 0, 610, 102, 1, 0, 0, 0, 611, 612, 5, 119, 0, 0, 612, 613, 5, 97, 0, 0, 613, 614, 5, 116, 0, 0, 614, 615, 5, 99, 0, 0, 615, 616, 5, 104, 0, 0, 616, 104, 1, 0, 0, 0, 617, 618, 5, 115, 0, 0, 618, 619, 5, 116, 0, 0, 619, 620, 5, 97, 0, 0, 620, 621, 5, 114, 0, 0, 621, 622, 5, 116, 0, 0, 622, 106, 1, 0, 0, 0, 623, 624, 5, 115, 0, 0, 624, 625, 5, 116, 0, 0, 625, 626, 5, 111, 0, 0, 626, 627, 5, 112, 0, 0, 627, 108, 1, 0, 0, 0, 628, 629, 5, 114, 0, 0, 629, 630, 5, 101, 0, 0, 630, 631, 5, 97, 0, 0, 631, 632, 5, 100, 0, 0, 632, 110, 1, 0, 0, 0, 633, 634, 5, 119, 0, 0, 634, 635, 5, 114, 0, 0, 635, 636, 5, 105, 0, 0, 636, 637, 5, 116, 0, 0, 637, 638, 5, 101, 0, 0, 638, 112, 1, 0, 0, 0, 639, 640, 5, 114, 0, 0, 640, 641, 5, 101, 0, 0, 641, 642, 5, 115, 0, 0, 642, 643, 5, 111, 0, 0, 643, 644, 5, 108, 0, 0, 644, 645, 5, 118, 0, 0, 645, 646, 5, 101, 0, 0, 646, 114, 1, 0, 0, 0, 647, 648, 5, 99, 0, 0, 648, 649, 5, 111, 0, 0, 649, 650, 5, 110, 0, 0, 650, 651, 5, 110, 0, 0, 651, 652, 5, 101, 0, 0, 652, 653, 5, 99, 0, 0, 653, 654, 5, 116, 0, 0, 654, 116, 1, 0, 0, 0, 655, 656, 5, 100, 0, 0, 656, 657, 5, 105, 0, 0, 657, 658, 5, 115, 0, 0, 658, 659, 5, 99, 0, 0, 659, 660, 5, 111, 0, 0, 660, 661, 5, 110, 0, 0, 661, 662, 5, 110, 0, 0, 662, 663, 5, 101, 0, 0, 663, 664, 5, 99, 0, 0, 664, 665, 5, 116, 0, 0, 665, 118, 1, 0, 0, 0, 666, 667, 5, 99, 0, 0, 667, 668, 5, 97, 0, 0, 668, 669, 5, 108, 0, 0, 669, 670, 5, 108, 0, 0, 670, 120, 1, 0, 0, 0, 671, 672, 5, 119, 0, 0, 672, 673, 5, 97, 0, 0, 673, 674, 5, 116, 0, 0, 674, 675, 5, 99, 0, 0, 675, 676, 5, 104, 0, 0, 676, 677, 5, 45, 0, 0, 677, 678, 5, 115, 0, 0, 678, 679, 5, 116, 0, 0, 679, 680, 5, 97, 0, 0, 680, 681, 5, 114, 0, 0, 681, 682, 5, 116, 0, 0, 682, 122, 1, 0, 0, 0, 683, 684, 5, 119, 0, 0, 684, 685, 5, 97, 0, 0, 685, 686, 5, 116, 0, 0, 686, 687, 5, 99, 0, 0, 687, 688, 5, 104, 0, 0, 688, 689, 5, 45, 0, 0, 689, 690, 5, 115, 0, 0, 690, 691, 5, 116, 0, 0, 691, 692, 5, 111, 0, 0, 692, 693, 5, 112, 0, 0, 693, 124, 1, 0, 0, 0, 694, 695, 5, 115, 0, 0, 695, 696, 5, 117, 0, 0, 696, 697, 5, 98, 0, 0, 697, 698, 5, 115, 0, 0, 698, 699, 5, 99, 0, 0, 699, 700, 5, 114, 0, 0, 700, 701, 5, 105, 0, 0, 701, 702, 5, 98, 0, 0, 702, 703, 5, 101, 0, 0, 703, 126, 1, 0, 0, 0, 704, 705, 5, 117, 0, 0, 705, 706, 5, 110, 0, 0, 706, 707, 5, 115, 0, 0, 707, 708, 5, 117, 0, 0, 708, 709, 5, 98, 0, 0, 709, 710, 5, 115, 0, 0, 710, 711, 5, 99, 0, 0, 711, 712, 5, 114, 0, 0, 712, 713, 5, 105, 0, 0, 713, 714, 5, 98, 0, 0, 714, 715, 5, 101, 0, 0, 715, 128, 1, 0, 0, 0, 716, 717, 5, 111, 0, 0, 717, 718, 5, 112, 0, 0, 718, 719, 5, 116, 0, 0, 719, 720, 5, 105, 0, 0, 720, 721, 5, 109, 0, 0, 721, 722, 5, 105, 0, 0, 722, 723, 5, 115, 0, 0, 723, 724, 5, 116, 0, 0, 724, 725, 5, 105, 0, 0, 725, 726, 5, 99, 0, 0, 726, 727, 5, 45, 0, 0, 727, 728, 5, 114, 0, 0, 728, 729, 5, 101, 0, 0, 729, 730, 5, 103, 0, 0, 730, 731, 5, 105, 0, 0, 731, 732, 5, 115, 0, 0, 732, 733, 5, 116, 0, 0, 733, 734, 5, 101, 0, 0, 734, 735, 5, 114, 0, 0, 735, 130, 1, 0, 0, 0, 736, 737, 5, 99, 0, 0, 737, 738, 5, 114, 0, 0, 738, 739, 5, 100, 0, 0, 739, 740, 5, 116, 0, 0, 740, 132, 1, 0, 0, 0, 741, 742, 5, 111, 0, 0, 742, 743, 5, 112, 0, 0, 743, 744, 5, 116, 0, 0, 744, 745, 5, 105, 0, 0, 745, 746, 5, 111, 0, 0, 746, 747, 5, 110, 0, 0, 747, 748, 5, 97, 0, 0, 748, 749, 5, 108, 0, 0, 749, 750, 5, 45, 0, 0, 750, 751, 5, 111, 0, 0, 751, 752, 5, 110, 0, 0, 752, 753, 5, 101, 0, 0, 753, 134, 1, 0, 0, 0, 754, 755, 5, 101, 0, 0, 755, 756, 5, 120, 0, 0, 756, 757, 5, 97, 0, 0, 757, 758, 5, 99, 0, 0, 758, 759, 5, 116, 0, 0, 759, 760, 5, 108, 0, 0, 760, 761, 5, 121, 0, 0, 761, 762, 5, 45, 0, 0, 762, 763, 5, 111, 0, 0, 763, 764, 5, 110, 0, 0, 764, 765, 5, 101, 0, 0, 765, 136, 1, 0, 0, 0, 766, 767, 5, 109, 0, 0, 767, 768, 5, 97, 0, 0, 768, 769, 5, 110, 0, 0, 769, 770, 5, 121, 0, 0, 770, 771, 5, 45, 0, 0, 771, 772, 5, 117, 0, 0, 772, 773, 5, 110, 0, 0, 773, 774, 5, 105, 0, 0, 774, 775, 5, 113, 0, 0, 775, 776, 5, 117, 0, 0, 776, 777, 5, 101, 0, 0, 777, 138, 1, 0, 0, 0, 778, 779, 5, 109, 0, 0, 779, 780, 5, 97, 0, 0, 780, 781, 5, 110, 0, 0, 781, 782, 5, 121, 0, 0, 782, 140, 1, 0, 0, 0, 783, 784, 5, 111, 0, 0, 784, 785, 5, 114, 0, 0, 785, 786, 5, 100, 0, 0, 786, 787, 5, 101, 0, 0, 787, 788, 5, 114, 0, 0, 788, 789, 5, 101, 0, 0, 789, 790, 5, 100, 0, 0, 790, 142, 1, 0, 0, 0, 791, 792, 5, 117, 0, 0, 792, 793, 5, 110, 0, 0, 793, 794, 5, 105, 0, 0, 794, 795, 5, 116, 0, 0, 795, 144, 1, 0, 0, 0, 796, 797, 5, 119, 0, 0, 797, 798, 5, 97, 0, 0, 798, 799, 5, 116, 0, 0, 799, 800, 5, 99, 0, 0, 800, 801, 5, 104, 0, 0, 801, 802, 5, 45, 0, 0, 802, 803, 5, 104, 0, 0, 803, 804, 5, 97, 0, 0, 804, 805, 5, 110, 0, 0, 805, 806, 5, 100, 0, 0, 806, 807, 5, 108, 0, 0, 807, 808, 5, 101, 0, 0, 808, 146, 1, 0, 0, 0, 809, 810, 5, 109, 0, 0, 810, 811, 5, 101, 0, 0, 811, 812, 5, 115, 0, 0, 812, 813, 5, 115, 0, 0, 813, 814, 5, 97, 0, 0, 814, 815, 5, 103, 0, 0, 815, 816, 5, 101, 0, 0, 816, 148, 1, 0, 0, 0, 817, 818, 5, 97, 0, 0, 818, 819, 5, 116, 0, 0, 819, 820, 5, 111, 0, 0, 820, 821, 5, 109, 0, 0, 821, 822, 5, 45, 0, 0, 822, 823, 5, 114, 0, 0, 823, 824, 5, 101, 0, 0, 824, 825, 5, 102, 0, 0, 825, 150, 1, 0, 0, 0, 826, 827, 5, 105, 0, 0, 827, 828, 5, 110, 0, 0, 828, 829, 5, 116, 0, 0, 829, 830, 5, 101, 0, 0, 830, 831, 5, 114, 0, 0, 831, 832, 5, 102, 0, 0, 832, 833, 5, 97, 0, 0, 833, 834, 5, 99, 0, 0, 834, 835, 5, 101, 0, 0, 835, 836, 5, 45, 0, 0, 836, 837, 5, 114, 0, 0, 837, 838, 5, 101, 0, 0, 838, 839, 5, 102, 0, 0, 839, 152, 1, 0, 0, 0, 840, 841, 5, 111, 0, 0, 841, 842, 5, 112, 0, 0, 842, 843, 5, 116, 0, 0, 843, 844, 5, 105, 0, 0, 844, 845, 5, 111, 0, 0, 845, 846, 5, 110, 0, 0, 846, 847, 5, 97, 0, 0, 847, 848, 5, 108, 0, 0, 848, 154, 1, 0, 0, 0, 849, 850, 5, 108, 0, 0, 850, 851, 5, 105, 0, 0, 851, 852, 5, 115, 0, 0, 852, 853, 5, 116, 0, 0, 853, 156, 1, 0, 0, 0, 854, 855, 5, 114, 0, 0, 855, 856, 5, 101, 0, 0, 856, 857, 5, 99, 0, 0, 857, 858, 5, 111, 0, 0, 858, 859, 5, 114, 0, 0, 859, 860, 5, 100, 0, 0, 860, 158, 1, 0, 0, 0, 861, 862, 5, 98, 0, 0, 862, 863, 5, 111, 0, 0, 863, 864, 5, 111, 0, 0, 864, 865, 5, 108, 0, 0, 865, 160, 1, 0, 0, 0, 866, 867, 5, 98, 0, 0, 867, 868, 5, 121, 0, 0, 868, 869, 5, 116, 0, 0, 869, 870, 5, 101, 0, 0, 870, 871, 5, 115, 0, 0, 871, 162, 1, 0, 0, 0, 872, 873, 5, 100, 0, 0, 873, 874, 5, 111, 0, 0, 874, 875, 5, 117, 0, 0, 875, 876, 5, 98, 0, 0, 876, 877, 5, 108, 0, 0, 877, 878, 5, 101, 0, 0, 878, 164, 1, 0, 0, 0, 879, 880, 5, 105, 0, 0, 880, 881, 5, 110, 0, 0, 881, 882, 5, 116, 0, 0, 882, 883, 5, 51, 0, 0, 883, 884, 5, 50, 0, 0, 884, 166, 1, 0, 0, 0, 885, 886, 5, 105, 0, 0, 886, 887, 5, 110, 0, 0, 887, 888, 5, 116, 0, 0, 888, 889, 5, 54, 0, 0, 889, 890, 5, 52, 0, 0, 890, 168, 1, 0, 0, 0, 891, 892, 5, 115, 0, 0, 892, 893, 5, 116, 0, 0, 893, 894, 5, 114, 0, 0, 894, 895, 5, 105, 0, 0, 895, 896, 5, 110, 0, 0, 896, 897, 5, 103, 0, 0, 897, 170, 1, 0, 0, 0, 898, 899, 5, 117, 0, 0, 899, 900, 5, 105, 0, 0, 900, 901, 5, 110, 0, 0, 901, 902, 5, 116, 0, 0, 902, 903, 5, 51, 0, 0, 903, 904, 5, 50, 0, 0, 904, 172, 1, 0, 0, 0, 905, 906, 5, 117, 0, 0, 906, 907, 5, 105, 0, 0, 907, 908, 5, 110, 0, 0, 908, 909, 5, 116, 0, 0, 909, 910, 5, 54, 0, 0, 910, 911, 5, 52, 0, 0, 911, 174, 1, 0, 0, 0, 912, 913, 5, 116, 0, 0, 913, 914, 5, 114, 0, 0, 914, 915, 5, 117, 0, 0, 915, 916, 5, 101, 0, 0, 916, 176, 1, 0, 0, 0, 917, 918, 5, 102, 0, 0, 918, 919, 5, 97, 0, 0, 919, 920, 5, 108, 0, 0, 920, 921, 5, 115, 0, 0, 921, 922, 5, 101, 0, 0, 922, 178, 1, 0, 0, 0, 923, 924, 5, 110, 0, 0, 924, 925, 5, 117, 0, 0, 925, 926, 5, 108, 0, 0, 926, 927, 5, 108, 0, 0, 927, 180, 1, 0, 0, 0, 928, 929, 5, 45, 0, 0, 929, 930, 5, 62, 0, 0, 930, 182, 1, 0, 0, 0, 931, 932, 5, 58, 0, 0, 932, 184, 1, 0, 0, 0, 933, 934, 5, 59, 0, 0, 934, 186, 1, 0, 0, 0, 935, 936, 5, 44, 0, 0, 936, 188, 1, 0, 0, 0, 937, 938, 5, 46, 0, 0, 938, 190, 1, 0, 0, 0, 939, 940, 5, 123, 0, 0, 940, 192, 1, 0, 0, 0, 941, 942, 5, 125, 0, 0, 942, 194, 1, 0, 0, 0, 943, 944, 5, 91, 0, 0, 944, 196, 1, 0, 0, 0, 945, 946, 5, 93, 0, 0, 946, 198, 1, 0, 0, 0, 947, 948, 5, 40, 0, 0, 948, 200, 1, 0, 0, 0, 949, 950, 5, 41, 0, 0, 950, 202, 1, 0, 0, 0, 951, 952, 5, 60, 0, 0, 952, 204, 1, 0, 0, 0, 953, 954, 5, 62, 0, 0, 954, 206, 1, 0, 0, 0, 955, 957, 5, 45, 0, 0, 956, 955, 1, 0, 0, 0, 956, 957, 1, 0, 0, 0, 957, 959, 1, 0, 0, 0, 958, 960, 7, 0, 0, 0, 959, 958, 1, 0, 0, 0, 960, 961, 1, 0, 0, 0, 961, 959, 1, 0, 0, 0, 961, 962, 1, 0, 0, 0, 962, 208, 1, 0, 0, 0, 963, 965, 5, 45, 0, 0, 964, 963, 1, 0, 0, 0, 964, 965, 1, 0, 0, 0, 965, 974, 1, 0, 0, 0, 966, 975, 5, 48, 0, 0, 967, 971, 7, 1, 0, 0, 968, 970, 7, 0, 0, 0, 969, 968, 1, 0, 0, 0, 970, 973, 1, 0, 0, 0, 971, 969, 1, 0, 0, 0, 971, 972, 1, 0, 0, 0, 972, 975, 1, 0, 0, 0, 973, 971, 1, 0, 0, 0, 974, 966, 1, 0, 0, 0, 974, 967, 1, 0, 0, 0, 975, 982, 1, 0, 0, 0, 976, 978, 5, 46, 0, 0, 977, 979, 7, 0, 0, 0, 978, 977, 1, 0, 0, 0, 979, 980, 1, 0, 0, 0, 980, 978, 1, 0, 0, 0, 980, 981, 1, 0, 0, 0, 981, 983, 1, 0, 0, 0, 982, 976, 1, 0, 0, 0, 982, 983, 1, 0, 0, 0, 983, 993, 1, 0, 0, 0, 984, 986, 7, 2, 0, 0, 985, 987, 7, 3, 0, 0, 986, 985, 1, 0, 0, 0, 986, 987, 1, 0, 0, 0, 987, 989, 1, 0, 0, 0, 988, 990, 7, 0, 0, 0, 989, 988, 1, 0, 0, 0, 990, 991, 1, 0, 0, 0, 991, 989, 1, 0, 0, 0, 991, 992, 1, 0, 0, 0, 992, 994, 1, 0, 0, 0, 993, 984, 1, 0, 0, 0, 993, 994, 1, 0, 0, 0, 994, 210, 1, 0, 0, 0, 995, 999, 7, 4, 0, 0, 996, 998, 7, 5, 0, 0, 997, 996, 1, 0, 0, 0, 998, 1001, 1, 0, 0, 0, 999, 997, 1, 0, 0, 0, 999, 1000, 1, 0, 0, 0, 1000, 212, 1, 0, 0, 0, 1001, 999, 1, 0, 0, 0, 1002, 1007, 5, 34, 0, 0, 1003, 1006, 3, 215, 107, 0, 1004, 1006, 8, 6, 0, 0, 1005, 1003, 1, 0, 0, 0, 1005, 1004, 1, 0, 0, 0, 1006, 1009, 1, 0, 0, 0, 1007, 1005, 1, 0, 0, 0, 1007, 1008, 1, 0, 0, 0, 1008, 1010, 1, 0, 0, 0, 1009, 1007, 1, 0, 0, 0, 1010, 1011, 5, 34, 0, 0, 1011, 214, 1, 0, 0, 0, 1012, 1020, 5, 92, 0, 0, 1013, 1021, 7, 7, 0, 0, 1014, 1015, 5, 117, 0, 0, 1015, 1016, 3, 217, 108, 0, 1016, 1017, 3, 217, 108, 0, 1017, 1018, 3, 217, 108, 0, 1018, 1019, 3, 217, 108, 0, 1019, 1021, 1, 0, 0, 0, 1020, 1013, 1, 0, 0, 0, 1020, 1014, 1, 0, 0, 0, 1021, 216, 1, 0, 0, 0, 1022, 1023, 7, 8, 0, 0, 1023, 218, 1, 0, 0, 0, 1024, 1025, 5, 47, 0, 0, 1025, 1026, 5, 47, 0, 0, 1026, 1030, 1, 0, 0, 0, 1027, 1029, 8, 9, 0, 0, 1028, 1027, 1, 0, 0, 0, 1029, 1032, 1, 0, 0, 0, 1030, 1028, 1, 0, 0, 0, 1030, 1031, 1, 0, 0, 0, 1031, 1033, 1, 0, 0, 0, 1032, 1030, 1, 0, 0, 0, 1033, 1034, 6, 109, 0, 0, 1034, 220, 1, 0, 0, 0, 1035, 1036, 5, 47, 0, 0, 1036, 1037, 5, 42, 0, 0, 1037, 1041, 1, 0, 0, 0, 1038, 1040, 9, 0, 0, 0, 1039, 1038, 1, 0, 0, 0, 1040, 1043, 1, 0, 0, 0, 1041, 1042, 1, 0, 0, 0, 1041, 1039, 1, 0, 0, 0, 1042, 1044, 1, 0, 0, 0, 1043, 1041, 1, 0, 0, 0, 1044, 1045, 5, 42, 0, 0, 1045, 1046, 5, 47, 0, 0, 1046, 1047, 1, 0, 0, 0, 1047, 1048, 6, 110, 0, 0, 1048, 222, 1, 0, 0, 0, 1049, 1051, 7, 10, 0, 0, 1050, 1049, 1, 0, 0, 0, 1051, 1052, 1, 0, 0, 0, 1052, 1050, 1, 0, 0, 0, 1052, 1053, 1, 0, 0, 0, 1053, 1054, 1, 0, 0, 0, 1054, 1055, 6, 111, 0, 0, 1055, 224, 1, 0, 0, 0, 18, 0, 956, 961, 964, 971, 974, 980, 982, 986, 991, 993, 999, 1005, 1007, 1020, 1030, 1041, 1052, 1, 0, 1, 0] \ No newline at end of file +[4, 0, 117, 1110, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 2, 88, 7, 88, 2, 89, 7, 89, 2, 90, 7, 90, 2, 91, 7, 91, 2, 92, 7, 92, 2, 93, 7, 93, 2, 94, 7, 94, 2, 95, 7, 95, 2, 96, 7, 96, 2, 97, 7, 97, 2, 98, 7, 98, 2, 99, 7, 99, 2, 100, 7, 100, 2, 101, 7, 101, 2, 102, 7, 102, 2, 103, 7, 103, 2, 104, 7, 104, 2, 105, 7, 105, 2, 106, 7, 106, 2, 107, 7, 107, 2, 108, 7, 108, 2, 109, 7, 109, 2, 110, 7, 110, 2, 111, 7, 111, 2, 112, 7, 112, 2, 113, 7, 113, 2, 114, 7, 114, 2, 115, 7, 115, 2, 116, 7, 116, 2, 117, 7, 117, 2, 118, 7, 118, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 83, 1, 83, 1, 83, 1, 83, 1, 83, 1, 83, 1, 83, 1, 84, 1, 84, 1, 84, 1, 84, 1, 84, 1, 85, 1, 85, 1, 85, 1, 85, 1, 85, 1, 85, 1, 86, 1, 86, 1, 86, 1, 86, 1, 86, 1, 86, 1, 86, 1, 87, 1, 87, 1, 87, 1, 87, 1, 87, 1, 87, 1, 88, 1, 88, 1, 88, 1, 88, 1, 88, 1, 88, 1, 89, 1, 89, 1, 89, 1, 89, 1, 89, 1, 89, 1, 89, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 91, 1, 91, 1, 91, 1, 91, 1, 91, 1, 91, 1, 91, 1, 92, 1, 92, 1, 92, 1, 92, 1, 92, 1, 93, 1, 93, 1, 93, 1, 93, 1, 93, 1, 93, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 95, 1, 95, 1, 95, 1, 96, 1, 96, 1, 97, 1, 97, 1, 98, 1, 98, 1, 99, 1, 99, 1, 100, 1, 100, 1, 101, 1, 101, 1, 102, 1, 102, 1, 103, 1, 103, 1, 104, 1, 104, 1, 105, 1, 105, 1, 106, 1, 106, 1, 107, 1, 107, 1, 108, 1, 108, 1, 109, 1, 109, 1, 110, 3, 110, 1011, 8, 110, 1, 110, 4, 110, 1014, 8, 110, 11, 110, 12, 110, 1015, 1, 111, 3, 111, 1019, 8, 111, 1, 111, 1, 111, 1, 111, 5, 111, 1024, 8, 111, 10, 111, 12, 111, 1027, 9, 111, 3, 111, 1029, 8, 111, 1, 111, 1, 111, 4, 111, 1033, 8, 111, 11, 111, 12, 111, 1034, 3, 111, 1037, 8, 111, 1, 111, 1, 111, 3, 111, 1041, 8, 111, 1, 111, 4, 111, 1044, 8, 111, 11, 111, 12, 111, 1045, 3, 111, 1048, 8, 111, 1, 112, 1, 112, 5, 112, 1052, 8, 112, 10, 112, 12, 112, 1055, 9, 112, 1, 113, 1, 113, 1, 113, 5, 113, 1060, 8, 113, 10, 113, 12, 113, 1063, 9, 113, 1, 113, 1, 113, 1, 114, 1, 114, 1, 114, 1, 114, 1, 114, 1, 114, 1, 114, 1, 114, 3, 114, 1075, 8, 114, 1, 115, 1, 115, 1, 116, 1, 116, 1, 116, 1, 116, 5, 116, 1083, 8, 116, 10, 116, 12, 116, 1086, 9, 116, 1, 116, 1, 116, 1, 117, 1, 117, 1, 117, 1, 117, 5, 117, 1094, 8, 117, 10, 117, 12, 117, 1097, 9, 117, 1, 117, 1, 117, 1, 117, 1, 117, 1, 117, 1, 118, 4, 118, 1105, 8, 118, 11, 118, 12, 118, 1106, 1, 118, 1, 118, 1, 1095, 0, 119, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 33, 67, 34, 69, 35, 71, 36, 73, 37, 75, 38, 77, 39, 79, 40, 81, 41, 83, 42, 85, 43, 87, 44, 89, 45, 91, 46, 93, 47, 95, 48, 97, 49, 99, 50, 101, 51, 103, 52, 105, 53, 107, 54, 109, 55, 111, 56, 113, 57, 115, 58, 117, 59, 119, 60, 121, 61, 123, 62, 125, 63, 127, 64, 129, 65, 131, 66, 133, 67, 135, 68, 137, 69, 139, 70, 141, 71, 143, 72, 145, 73, 147, 74, 149, 75, 151, 76, 153, 77, 155, 78, 157, 79, 159, 80, 161, 81, 163, 82, 165, 83, 167, 84, 169, 85, 171, 86, 173, 87, 175, 88, 177, 89, 179, 90, 181, 91, 183, 92, 185, 93, 187, 94, 189, 95, 191, 96, 193, 97, 195, 98, 197, 99, 199, 100, 201, 101, 203, 102, 205, 103, 207, 104, 209, 105, 211, 106, 213, 107, 215, 108, 217, 109, 219, 110, 221, 111, 223, 112, 225, 113, 227, 114, 229, 0, 231, 0, 233, 115, 235, 116, 237, 117, 1, 0, 11, 1, 0, 48, 57, 1, 0, 49, 57, 2, 0, 69, 69, 101, 101, 2, 0, 43, 43, 45, 45, 3, 0, 65, 90, 95, 95, 97, 122, 4, 0, 48, 57, 65, 90, 95, 95, 97, 122, 4, 0, 10, 10, 13, 13, 34, 34, 92, 92, 8, 0, 34, 34, 47, 47, 92, 92, 98, 98, 102, 102, 110, 110, 114, 114, 116, 116, 3, 0, 48, 57, 65, 70, 97, 102, 2, 0, 10, 10, 13, 13, 3, 0, 9, 10, 13, 13, 32, 32, 1124, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0, 65, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 73, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, 81, 1, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0, 89, 1, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 93, 1, 0, 0, 0, 0, 95, 1, 0, 0, 0, 0, 97, 1, 0, 0, 0, 0, 99, 1, 0, 0, 0, 0, 101, 1, 0, 0, 0, 0, 103, 1, 0, 0, 0, 0, 105, 1, 0, 0, 0, 0, 107, 1, 0, 0, 0, 0, 109, 1, 0, 0, 0, 0, 111, 1, 0, 0, 0, 0, 113, 1, 0, 0, 0, 0, 115, 1, 0, 0, 0, 0, 117, 1, 0, 0, 0, 0, 119, 1, 0, 0, 0, 0, 121, 1, 0, 0, 0, 0, 123, 1, 0, 0, 0, 0, 125, 1, 0, 0, 0, 0, 127, 1, 0, 0, 0, 0, 129, 1, 0, 0, 0, 0, 131, 1, 0, 0, 0, 0, 133, 1, 0, 0, 0, 0, 135, 1, 0, 0, 0, 0, 137, 1, 0, 0, 0, 0, 139, 1, 0, 0, 0, 0, 141, 1, 0, 0, 0, 0, 143, 1, 0, 0, 0, 0, 145, 1, 0, 0, 0, 0, 147, 1, 0, 0, 0, 0, 149, 1, 0, 0, 0, 0, 151, 1, 0, 0, 0, 0, 153, 1, 0, 0, 0, 0, 155, 1, 0, 0, 0, 0, 157, 1, 0, 0, 0, 0, 159, 1, 0, 0, 0, 0, 161, 1, 0, 0, 0, 0, 163, 1, 0, 0, 0, 0, 165, 1, 0, 0, 0, 0, 167, 1, 0, 0, 0, 0, 169, 1, 0, 0, 0, 0, 171, 1, 0, 0, 0, 0, 173, 1, 0, 0, 0, 0, 175, 1, 0, 0, 0, 0, 177, 1, 0, 0, 0, 0, 179, 1, 0, 0, 0, 0, 181, 1, 0, 0, 0, 0, 183, 1, 0, 0, 0, 0, 185, 1, 0, 0, 0, 0, 187, 1, 0, 0, 0, 0, 189, 1, 0, 0, 0, 0, 191, 1, 0, 0, 0, 0, 193, 1, 0, 0, 0, 0, 195, 1, 0, 0, 0, 0, 197, 1, 0, 0, 0, 0, 199, 1, 0, 0, 0, 0, 201, 1, 0, 0, 0, 0, 203, 1, 0, 0, 0, 0, 205, 1, 0, 0, 0, 0, 207, 1, 0, 0, 0, 0, 209, 1, 0, 0, 0, 0, 211, 1, 0, 0, 0, 0, 213, 1, 0, 0, 0, 0, 215, 1, 0, 0, 0, 0, 217, 1, 0, 0, 0, 0, 219, 1, 0, 0, 0, 0, 221, 1, 0, 0, 0, 0, 223, 1, 0, 0, 0, 0, 225, 1, 0, 0, 0, 0, 227, 1, 0, 0, 0, 0, 233, 1, 0, 0, 0, 0, 235, 1, 0, 0, 0, 0, 237, 1, 0, 0, 0, 1, 239, 1, 0, 0, 0, 3, 249, 1, 0, 0, 0, 5, 254, 1, 0, 0, 0, 7, 261, 1, 0, 0, 0, 9, 270, 1, 0, 0, 0, 11, 281, 1, 0, 0, 0, 13, 285, 1, 0, 0, 0, 15, 294, 1, 0, 0, 0, 17, 301, 1, 0, 0, 0, 19, 310, 1, 0, 0, 0, 21, 315, 1, 0, 0, 0, 23, 325, 1, 0, 0, 0, 25, 336, 1, 0, 0, 0, 27, 344, 1, 0, 0, 0, 29, 350, 1, 0, 0, 0, 31, 359, 1, 0, 0, 0, 33, 369, 1, 0, 0, 0, 35, 378, 1, 0, 0, 0, 37, 390, 1, 0, 0, 0, 39, 401, 1, 0, 0, 0, 41, 407, 1, 0, 0, 0, 43, 415, 1, 0, 0, 0, 45, 418, 1, 0, 0, 0, 47, 423, 1, 0, 0, 0, 49, 426, 1, 0, 0, 0, 51, 434, 1, 0, 0, 0, 53, 441, 1, 0, 0, 0, 55, 447, 1, 0, 0, 0, 57, 452, 1, 0, 0, 0, 59, 463, 1, 0, 0, 0, 61, 468, 1, 0, 0, 0, 63, 474, 1, 0, 0, 0, 65, 478, 1, 0, 0, 0, 67, 490, 1, 0, 0, 0, 69, 493, 1, 0, 0, 0, 71, 500, 1, 0, 0, 0, 73, 503, 1, 0, 0, 0, 75, 510, 1, 0, 0, 0, 77, 518, 1, 0, 0, 0, 79, 525, 1, 0, 0, 0, 81, 536, 1, 0, 0, 0, 83, 543, 1, 0, 0, 0, 85, 552, 1, 0, 0, 0, 87, 567, 1, 0, 0, 0, 89, 577, 1, 0, 0, 0, 91, 590, 1, 0, 0, 0, 93, 596, 1, 0, 0, 0, 95, 613, 1, 0, 0, 0, 97, 616, 1, 0, 0, 0, 99, 620, 1, 0, 0, 0, 101, 625, 1, 0, 0, 0, 103, 631, 1, 0, 0, 0, 105, 640, 1, 0, 0, 0, 107, 649, 1, 0, 0, 0, 109, 653, 1, 0, 0, 0, 111, 657, 1, 0, 0, 0, 113, 661, 1, 0, 0, 0, 115, 667, 1, 0, 0, 0, 117, 673, 1, 0, 0, 0, 119, 678, 1, 0, 0, 0, 121, 683, 1, 0, 0, 0, 123, 689, 1, 0, 0, 0, 125, 697, 1, 0, 0, 0, 127, 705, 1, 0, 0, 0, 129, 716, 1, 0, 0, 0, 131, 721, 1, 0, 0, 0, 133, 733, 1, 0, 0, 0, 135, 744, 1, 0, 0, 0, 137, 754, 1, 0, 0, 0, 139, 766, 1, 0, 0, 0, 141, 786, 1, 0, 0, 0, 143, 791, 1, 0, 0, 0, 145, 804, 1, 0, 0, 0, 147, 816, 1, 0, 0, 0, 149, 828, 1, 0, 0, 0, 151, 833, 1, 0, 0, 0, 153, 841, 1, 0, 0, 0, 155, 846, 1, 0, 0, 0, 157, 859, 1, 0, 0, 0, 159, 867, 1, 0, 0, 0, 161, 876, 1, 0, 0, 0, 163, 890, 1, 0, 0, 0, 165, 899, 1, 0, 0, 0, 167, 904, 1, 0, 0, 0, 169, 911, 1, 0, 0, 0, 171, 916, 1, 0, 0, 0, 173, 922, 1, 0, 0, 0, 175, 929, 1, 0, 0, 0, 177, 935, 1, 0, 0, 0, 179, 941, 1, 0, 0, 0, 181, 948, 1, 0, 0, 0, 183, 955, 1, 0, 0, 0, 185, 962, 1, 0, 0, 0, 187, 967, 1, 0, 0, 0, 189, 973, 1, 0, 0, 0, 191, 978, 1, 0, 0, 0, 193, 981, 1, 0, 0, 0, 195, 983, 1, 0, 0, 0, 197, 985, 1, 0, 0, 0, 199, 987, 1, 0, 0, 0, 201, 989, 1, 0, 0, 0, 203, 991, 1, 0, 0, 0, 205, 993, 1, 0, 0, 0, 207, 995, 1, 0, 0, 0, 209, 997, 1, 0, 0, 0, 211, 999, 1, 0, 0, 0, 213, 1001, 1, 0, 0, 0, 215, 1003, 1, 0, 0, 0, 217, 1005, 1, 0, 0, 0, 219, 1007, 1, 0, 0, 0, 221, 1010, 1, 0, 0, 0, 223, 1018, 1, 0, 0, 0, 225, 1049, 1, 0, 0, 0, 227, 1056, 1, 0, 0, 0, 229, 1066, 1, 0, 0, 0, 231, 1076, 1, 0, 0, 0, 233, 1078, 1, 0, 0, 0, 235, 1089, 1, 0, 0, 0, 237, 1104, 1, 0, 0, 0, 239, 240, 5, 119, 0, 0, 240, 241, 5, 111, 0, 0, 241, 242, 5, 114, 0, 0, 242, 243, 5, 107, 0, 0, 243, 244, 5, 115, 0, 0, 244, 245, 5, 112, 0, 0, 245, 246, 5, 97, 0, 0, 246, 247, 5, 99, 0, 0, 247, 248, 5, 101, 0, 0, 248, 2, 1, 0, 0, 0, 249, 250, 5, 116, 0, 0, 250, 251, 5, 121, 0, 0, 251, 252, 5, 112, 0, 0, 252, 253, 5, 101, 0, 0, 253, 4, 1, 0, 0, 0, 254, 255, 5, 111, 0, 0, 255, 256, 5, 98, 0, 0, 256, 257, 5, 106, 0, 0, 257, 258, 5, 101, 0, 0, 258, 259, 5, 99, 0, 0, 259, 260, 5, 116, 0, 0, 260, 6, 1, 0, 0, 0, 261, 262, 5, 115, 0, 0, 262, 263, 5, 116, 0, 0, 263, 264, 5, 111, 0, 0, 264, 265, 5, 114, 0, 0, 265, 266, 5, 97, 0, 0, 266, 267, 5, 98, 0, 0, 267, 268, 5, 108, 0, 0, 268, 269, 5, 101, 0, 0, 269, 8, 1, 0, 0, 0, 270, 271, 5, 105, 0, 0, 271, 272, 5, 109, 0, 0, 272, 273, 5, 112, 0, 0, 273, 274, 5, 108, 0, 0, 274, 275, 5, 101, 0, 0, 275, 276, 5, 109, 0, 0, 276, 277, 5, 101, 0, 0, 277, 278, 5, 110, 0, 0, 278, 279, 5, 116, 0, 0, 279, 280, 5, 115, 0, 0, 280, 10, 1, 0, 0, 0, 281, 282, 5, 114, 0, 0, 282, 283, 5, 101, 0, 0, 283, 284, 5, 102, 0, 0, 284, 12, 1, 0, 0, 0, 285, 286, 5, 102, 0, 0, 286, 287, 5, 114, 0, 0, 287, 288, 5, 97, 0, 0, 288, 289, 5, 103, 0, 0, 289, 290, 5, 109, 0, 0, 290, 291, 5, 101, 0, 0, 291, 292, 5, 110, 0, 0, 292, 293, 5, 116, 0, 0, 293, 14, 1, 0, 0, 0, 294, 295, 5, 105, 0, 0, 295, 296, 5, 109, 0, 0, 296, 297, 5, 112, 0, 0, 297, 298, 5, 111, 0, 0, 298, 299, 5, 114, 0, 0, 299, 300, 5, 116, 0, 0, 300, 16, 1, 0, 0, 0, 301, 302, 5, 101, 0, 0, 302, 303, 5, 120, 0, 0, 303, 304, 5, 116, 0, 0, 304, 305, 5, 101, 0, 0, 305, 306, 5, 114, 0, 0, 306, 307, 5, 110, 0, 0, 307, 308, 5, 97, 0, 0, 308, 309, 5, 108, 0, 0, 309, 18, 1, 0, 0, 0, 310, 311, 5, 97, 0, 0, 311, 312, 5, 116, 0, 0, 312, 313, 5, 111, 0, 0, 313, 314, 5, 109, 0, 0, 314, 20, 1, 0, 0, 0, 315, 316, 5, 105, 0, 0, 316, 317, 5, 110, 0, 0, 317, 318, 5, 116, 0, 0, 318, 319, 5, 101, 0, 0, 319, 320, 5, 114, 0, 0, 320, 321, 5, 102, 0, 0, 321, 322, 5, 97, 0, 0, 322, 323, 5, 99, 0, 0, 323, 324, 5, 101, 0, 0, 324, 22, 1, 0, 0, 0, 325, 326, 5, 105, 0, 0, 326, 327, 5, 110, 0, 0, 327, 328, 5, 116, 0, 0, 328, 329, 5, 101, 0, 0, 329, 330, 5, 114, 0, 0, 330, 331, 5, 102, 0, 0, 331, 332, 5, 97, 0, 0, 332, 333, 5, 99, 0, 0, 333, 334, 5, 101, 0, 0, 334, 335, 5, 115, 0, 0, 335, 24, 1, 0, 0, 0, 336, 337, 5, 112, 0, 0, 337, 338, 5, 97, 0, 0, 338, 339, 5, 99, 0, 0, 339, 340, 5, 107, 0, 0, 340, 341, 5, 97, 0, 0, 341, 342, 5, 103, 0, 0, 342, 343, 5, 101, 0, 0, 343, 26, 1, 0, 0, 0, 344, 345, 5, 118, 0, 0, 345, 346, 5, 97, 0, 0, 346, 347, 5, 108, 0, 0, 347, 348, 5, 117, 0, 0, 348, 349, 5, 101, 0, 0, 349, 28, 1, 0, 0, 0, 350, 351, 5, 114, 0, 0, 351, 352, 5, 101, 0, 0, 352, 353, 5, 108, 0, 0, 353, 354, 5, 97, 0, 0, 354, 355, 5, 116, 0, 0, 355, 356, 5, 105, 0, 0, 356, 357, 5, 111, 0, 0, 357, 358, 5, 110, 0, 0, 358, 30, 1, 0, 0, 0, 359, 360, 5, 111, 0, 0, 360, 361, 5, 112, 0, 0, 361, 362, 5, 101, 0, 0, 362, 363, 5, 114, 0, 0, 363, 364, 5, 97, 0, 0, 364, 365, 5, 116, 0, 0, 365, 366, 5, 105, 0, 0, 366, 367, 5, 111, 0, 0, 367, 368, 5, 110, 0, 0, 368, 32, 1, 0, 0, 0, 369, 370, 5, 102, 0, 0, 370, 371, 5, 117, 0, 0, 371, 372, 5, 110, 0, 0, 372, 373, 5, 99, 0, 0, 373, 374, 5, 116, 0, 0, 374, 375, 5, 105, 0, 0, 375, 376, 5, 111, 0, 0, 376, 377, 5, 110, 0, 0, 377, 34, 1, 0, 0, 0, 378, 379, 5, 99, 0, 0, 379, 380, 5, 111, 0, 0, 380, 381, 5, 110, 0, 0, 381, 382, 5, 115, 0, 0, 382, 383, 5, 116, 0, 0, 383, 384, 5, 114, 0, 0, 384, 385, 5, 117, 0, 0, 385, 386, 5, 99, 0, 0, 386, 387, 5, 116, 0, 0, 387, 388, 5, 111, 0, 0, 388, 389, 5, 114, 0, 0, 389, 36, 1, 0, 0, 0, 390, 391, 5, 99, 0, 0, 391, 392, 5, 111, 0, 0, 392, 393, 5, 110, 0, 0, 393, 394, 5, 115, 0, 0, 394, 395, 5, 116, 0, 0, 395, 396, 5, 114, 0, 0, 396, 397, 5, 117, 0, 0, 397, 398, 5, 99, 0, 0, 398, 399, 5, 116, 0, 0, 399, 400, 5, 115, 0, 0, 400, 38, 1, 0, 0, 0, 401, 402, 5, 105, 0, 0, 402, 403, 5, 110, 0, 0, 403, 404, 5, 112, 0, 0, 404, 405, 5, 117, 0, 0, 405, 406, 5, 116, 0, 0, 406, 40, 1, 0, 0, 0, 407, 408, 5, 99, 0, 0, 408, 409, 5, 111, 0, 0, 409, 410, 5, 110, 0, 0, 410, 411, 5, 102, 0, 0, 411, 412, 5, 111, 0, 0, 412, 413, 5, 114, 0, 0, 413, 414, 5, 109, 0, 0, 414, 42, 1, 0, 0, 0, 415, 416, 5, 97, 0, 0, 416, 417, 5, 115, 0, 0, 417, 44, 1, 0, 0, 0, 418, 419, 5, 98, 0, 0, 419, 420, 5, 105, 0, 0, 420, 421, 5, 110, 0, 0, 421, 422, 5, 100, 0, 0, 422, 46, 1, 0, 0, 0, 423, 424, 5, 116, 0, 0, 424, 425, 5, 111, 0, 0, 425, 48, 1, 0, 0, 0, 426, 427, 5, 112, 0, 0, 427, 428, 5, 114, 0, 0, 428, 429, 5, 105, 0, 0, 429, 430, 5, 118, 0, 0, 430, 431, 5, 97, 0, 0, 431, 432, 5, 116, 0, 0, 432, 433, 5, 101, 0, 0, 433, 50, 1, 0, 0, 0, 434, 435, 5, 115, 0, 0, 435, 436, 5, 104, 0, 0, 436, 437, 5, 97, 0, 0, 437, 438, 5, 114, 0, 0, 438, 439, 5, 101, 0, 0, 439, 440, 5, 100, 0, 0, 440, 52, 1, 0, 0, 0, 441, 442, 5, 115, 0, 0, 442, 443, 5, 116, 0, 0, 443, 444, 5, 97, 0, 0, 444, 445, 5, 116, 0, 0, 445, 446, 5, 101, 0, 0, 446, 54, 1, 0, 0, 0, 447, 448, 5, 101, 0, 0, 448, 449, 5, 100, 0, 0, 449, 450, 5, 103, 0, 0, 450, 451, 5, 101, 0, 0, 451, 56, 1, 0, 0, 0, 452, 453, 5, 112, 0, 0, 453, 454, 5, 114, 0, 0, 454, 455, 5, 111, 0, 0, 455, 456, 5, 106, 0, 0, 456, 457, 5, 101, 0, 0, 457, 458, 5, 99, 0, 0, 458, 459, 5, 116, 0, 0, 459, 460, 5, 105, 0, 0, 460, 461, 5, 111, 0, 0, 461, 462, 5, 110, 0, 0, 462, 58, 1, 0, 0, 0, 463, 464, 5, 119, 0, 0, 464, 465, 5, 105, 0, 0, 465, 466, 5, 116, 0, 0, 466, 467, 5, 104, 0, 0, 467, 60, 1, 0, 0, 0, 468, 469, 5, 117, 0, 0, 469, 470, 5, 115, 0, 0, 470, 471, 5, 105, 0, 0, 471, 472, 5, 110, 0, 0, 472, 473, 5, 103, 0, 0, 473, 62, 1, 0, 0, 0, 474, 475, 5, 118, 0, 0, 475, 476, 5, 105, 0, 0, 476, 477, 5, 97, 0, 0, 477, 64, 1, 0, 0, 0, 478, 479, 5, 109, 0, 0, 479, 480, 5, 97, 0, 0, 480, 481, 5, 116, 0, 0, 481, 482, 5, 101, 0, 0, 482, 483, 5, 114, 0, 0, 483, 484, 5, 105, 0, 0, 484, 485, 5, 97, 0, 0, 485, 486, 5, 108, 0, 0, 486, 487, 5, 105, 0, 0, 487, 488, 5, 122, 0, 0, 488, 489, 5, 101, 0, 0, 489, 66, 1, 0, 0, 0, 490, 491, 5, 105, 0, 0, 491, 492, 5, 102, 0, 0, 492, 68, 1, 0, 0, 0, 493, 494, 5, 97, 0, 0, 494, 495, 5, 98, 0, 0, 495, 496, 5, 115, 0, 0, 496, 497, 5, 101, 0, 0, 497, 498, 5, 110, 0, 0, 498, 499, 5, 116, 0, 0, 499, 70, 1, 0, 0, 0, 500, 501, 5, 111, 0, 0, 501, 502, 5, 110, 0, 0, 502, 72, 1, 0, 0, 0, 503, 504, 5, 112, 0, 0, 504, 505, 5, 111, 0, 0, 505, 506, 5, 108, 0, 0, 506, 507, 5, 105, 0, 0, 507, 508, 5, 99, 0, 0, 508, 509, 5, 121, 0, 0, 509, 74, 1, 0, 0, 0, 510, 511, 5, 100, 0, 0, 511, 512, 5, 101, 0, 0, 512, 513, 5, 102, 0, 0, 513, 514, 5, 97, 0, 0, 514, 515, 5, 117, 0, 0, 515, 516, 5, 108, 0, 0, 516, 517, 5, 116, 0, 0, 517, 76, 1, 0, 0, 0, 518, 519, 5, 115, 0, 0, 519, 520, 5, 111, 0, 0, 520, 521, 5, 117, 0, 0, 521, 522, 5, 114, 0, 0, 522, 523, 5, 99, 0, 0, 523, 524, 5, 101, 0, 0, 524, 78, 1, 0, 0, 0, 525, 526, 5, 114, 0, 0, 526, 527, 5, 101, 0, 0, 527, 528, 5, 112, 0, 0, 528, 529, 5, 111, 0, 0, 529, 530, 5, 115, 0, 0, 530, 531, 5, 105, 0, 0, 531, 532, 5, 116, 0, 0, 532, 533, 5, 111, 0, 0, 533, 534, 5, 114, 0, 0, 534, 535, 5, 121, 0, 0, 535, 80, 1, 0, 0, 0, 536, 537, 5, 99, 0, 0, 537, 538, 5, 111, 0, 0, 538, 539, 5, 109, 0, 0, 539, 540, 5, 109, 0, 0, 540, 541, 5, 105, 0, 0, 541, 542, 5, 116, 0, 0, 542, 82, 1, 0, 0, 0, 543, 544, 5, 114, 0, 0, 544, 545, 5, 101, 0, 0, 545, 546, 5, 118, 0, 0, 546, 547, 5, 105, 0, 0, 547, 548, 5, 115, 0, 0, 548, 549, 5, 105, 0, 0, 549, 550, 5, 111, 0, 0, 550, 551, 5, 110, 0, 0, 551, 84, 1, 0, 0, 0, 552, 553, 5, 115, 0, 0, 553, 554, 5, 101, 0, 0, 554, 555, 5, 109, 0, 0, 555, 556, 5, 97, 0, 0, 556, 557, 5, 110, 0, 0, 557, 558, 5, 116, 0, 0, 558, 559, 5, 105, 0, 0, 559, 560, 5, 99, 0, 0, 560, 561, 5, 45, 0, 0, 561, 562, 5, 109, 0, 0, 562, 563, 5, 97, 0, 0, 563, 564, 5, 106, 0, 0, 564, 565, 5, 111, 0, 0, 565, 566, 5, 114, 0, 0, 566, 86, 1, 0, 0, 0, 567, 568, 5, 111, 0, 0, 568, 569, 5, 110, 0, 0, 569, 570, 5, 45, 0, 0, 570, 571, 5, 100, 0, 0, 571, 572, 5, 101, 0, 0, 572, 573, 5, 108, 0, 0, 573, 574, 5, 101, 0, 0, 574, 575, 5, 116, 0, 0, 575, 576, 5, 101, 0, 0, 576, 88, 1, 0, 0, 0, 577, 578, 5, 114, 0, 0, 578, 579, 5, 101, 0, 0, 579, 580, 5, 116, 0, 0, 580, 581, 5, 97, 0, 0, 581, 582, 5, 105, 0, 0, 582, 583, 5, 110, 0, 0, 583, 584, 5, 45, 0, 0, 584, 585, 5, 111, 0, 0, 585, 586, 5, 116, 0, 0, 586, 587, 5, 104, 0, 0, 587, 588, 5, 101, 0, 0, 588, 589, 5, 114, 0, 0, 589, 90, 1, 0, 0, 0, 590, 591, 5, 107, 0, 0, 591, 592, 5, 101, 0, 0, 592, 593, 5, 121, 0, 0, 593, 594, 5, 101, 0, 0, 594, 595, 5, 100, 0, 0, 595, 92, 1, 0, 0, 0, 596, 597, 5, 112, 0, 0, 597, 598, 5, 117, 0, 0, 598, 599, 5, 98, 0, 0, 599, 600, 5, 108, 0, 0, 600, 601, 5, 105, 0, 0, 601, 602, 5, 99, 0, 0, 602, 603, 5, 45, 0, 0, 603, 604, 5, 116, 0, 0, 604, 605, 5, 114, 0, 0, 605, 606, 5, 97, 0, 0, 606, 607, 5, 118, 0, 0, 607, 608, 5, 101, 0, 0, 608, 609, 5, 114, 0, 0, 609, 610, 5, 115, 0, 0, 610, 611, 5, 97, 0, 0, 611, 612, 5, 108, 0, 0, 612, 94, 1, 0, 0, 0, 613, 614, 5, 105, 0, 0, 614, 615, 5, 100, 0, 0, 615, 96, 1, 0, 0, 0, 616, 617, 5, 100, 0, 0, 617, 618, 5, 111, 0, 0, 618, 619, 5, 99, 0, 0, 619, 98, 1, 0, 0, 0, 620, 621, 5, 109, 0, 0, 621, 622, 5, 111, 0, 0, 622, 623, 5, 100, 0, 0, 623, 624, 5, 101, 0, 0, 624, 100, 1, 0, 0, 0, 625, 626, 5, 101, 0, 0, 626, 627, 5, 109, 0, 0, 627, 628, 5, 105, 0, 0, 628, 629, 5, 116, 0, 0, 629, 630, 5, 115, 0, 0, 630, 102, 1, 0, 0, 0, 631, 632, 5, 114, 0, 0, 632, 633, 5, 101, 0, 0, 633, 634, 5, 99, 0, 0, 634, 635, 5, 101, 0, 0, 635, 636, 5, 105, 0, 0, 636, 637, 5, 118, 0, 0, 637, 638, 5, 101, 0, 0, 638, 639, 5, 114, 0, 0, 639, 104, 1, 0, 0, 0, 640, 641, 5, 114, 0, 0, 641, 642, 5, 101, 0, 0, 642, 643, 5, 113, 0, 0, 643, 644, 5, 117, 0, 0, 644, 645, 5, 105, 0, 0, 645, 646, 5, 114, 0, 0, 646, 647, 5, 101, 0, 0, 647, 648, 5, 115, 0, 0, 648, 106, 1, 0, 0, 0, 649, 650, 5, 97, 0, 0, 650, 651, 5, 110, 0, 0, 651, 652, 5, 121, 0, 0, 652, 108, 1, 0, 0, 0, 653, 654, 5, 103, 0, 0, 654, 655, 5, 101, 0, 0, 655, 656, 5, 116, 0, 0, 656, 110, 1, 0, 0, 0, 657, 658, 5, 115, 0, 0, 658, 659, 5, 101, 0, 0, 659, 660, 5, 116, 0, 0, 660, 112, 1, 0, 0, 0, 661, 662, 5, 119, 0, 0, 662, 663, 5, 97, 0, 0, 663, 664, 5, 116, 0, 0, 664, 665, 5, 99, 0, 0, 665, 666, 5, 104, 0, 0, 666, 114, 1, 0, 0, 0, 667, 668, 5, 115, 0, 0, 668, 669, 5, 116, 0, 0, 669, 670, 5, 97, 0, 0, 670, 671, 5, 114, 0, 0, 671, 672, 5, 116, 0, 0, 672, 116, 1, 0, 0, 0, 673, 674, 5, 115, 0, 0, 674, 675, 5, 116, 0, 0, 675, 676, 5, 111, 0, 0, 676, 677, 5, 112, 0, 0, 677, 118, 1, 0, 0, 0, 678, 679, 5, 114, 0, 0, 679, 680, 5, 101, 0, 0, 680, 681, 5, 97, 0, 0, 681, 682, 5, 100, 0, 0, 682, 120, 1, 0, 0, 0, 683, 684, 5, 119, 0, 0, 684, 685, 5, 114, 0, 0, 685, 686, 5, 105, 0, 0, 686, 687, 5, 116, 0, 0, 687, 688, 5, 101, 0, 0, 688, 122, 1, 0, 0, 0, 689, 690, 5, 114, 0, 0, 690, 691, 5, 101, 0, 0, 691, 692, 5, 115, 0, 0, 692, 693, 5, 111, 0, 0, 693, 694, 5, 108, 0, 0, 694, 695, 5, 118, 0, 0, 695, 696, 5, 101, 0, 0, 696, 124, 1, 0, 0, 0, 697, 698, 5, 99, 0, 0, 698, 699, 5, 111, 0, 0, 699, 700, 5, 110, 0, 0, 700, 701, 5, 110, 0, 0, 701, 702, 5, 101, 0, 0, 702, 703, 5, 99, 0, 0, 703, 704, 5, 116, 0, 0, 704, 126, 1, 0, 0, 0, 705, 706, 5, 100, 0, 0, 706, 707, 5, 105, 0, 0, 707, 708, 5, 115, 0, 0, 708, 709, 5, 99, 0, 0, 709, 710, 5, 111, 0, 0, 710, 711, 5, 110, 0, 0, 711, 712, 5, 110, 0, 0, 712, 713, 5, 101, 0, 0, 713, 714, 5, 99, 0, 0, 714, 715, 5, 116, 0, 0, 715, 128, 1, 0, 0, 0, 716, 717, 5, 99, 0, 0, 717, 718, 5, 97, 0, 0, 718, 719, 5, 108, 0, 0, 719, 720, 5, 108, 0, 0, 720, 130, 1, 0, 0, 0, 721, 722, 5, 119, 0, 0, 722, 723, 5, 97, 0, 0, 723, 724, 5, 116, 0, 0, 724, 725, 5, 99, 0, 0, 725, 726, 5, 104, 0, 0, 726, 727, 5, 45, 0, 0, 727, 728, 5, 115, 0, 0, 728, 729, 5, 116, 0, 0, 729, 730, 5, 97, 0, 0, 730, 731, 5, 114, 0, 0, 731, 732, 5, 116, 0, 0, 732, 132, 1, 0, 0, 0, 733, 734, 5, 119, 0, 0, 734, 735, 5, 97, 0, 0, 735, 736, 5, 116, 0, 0, 736, 737, 5, 99, 0, 0, 737, 738, 5, 104, 0, 0, 738, 739, 5, 45, 0, 0, 739, 740, 5, 115, 0, 0, 740, 741, 5, 116, 0, 0, 741, 742, 5, 111, 0, 0, 742, 743, 5, 112, 0, 0, 743, 134, 1, 0, 0, 0, 744, 745, 5, 115, 0, 0, 745, 746, 5, 117, 0, 0, 746, 747, 5, 98, 0, 0, 747, 748, 5, 115, 0, 0, 748, 749, 5, 99, 0, 0, 749, 750, 5, 114, 0, 0, 750, 751, 5, 105, 0, 0, 751, 752, 5, 98, 0, 0, 752, 753, 5, 101, 0, 0, 753, 136, 1, 0, 0, 0, 754, 755, 5, 117, 0, 0, 755, 756, 5, 110, 0, 0, 756, 757, 5, 115, 0, 0, 757, 758, 5, 117, 0, 0, 758, 759, 5, 98, 0, 0, 759, 760, 5, 115, 0, 0, 760, 761, 5, 99, 0, 0, 761, 762, 5, 114, 0, 0, 762, 763, 5, 105, 0, 0, 763, 764, 5, 98, 0, 0, 764, 765, 5, 101, 0, 0, 765, 138, 1, 0, 0, 0, 766, 767, 5, 111, 0, 0, 767, 768, 5, 112, 0, 0, 768, 769, 5, 116, 0, 0, 769, 770, 5, 105, 0, 0, 770, 771, 5, 109, 0, 0, 771, 772, 5, 105, 0, 0, 772, 773, 5, 115, 0, 0, 773, 774, 5, 116, 0, 0, 774, 775, 5, 105, 0, 0, 775, 776, 5, 99, 0, 0, 776, 777, 5, 45, 0, 0, 777, 778, 5, 114, 0, 0, 778, 779, 5, 101, 0, 0, 779, 780, 5, 103, 0, 0, 780, 781, 5, 105, 0, 0, 781, 782, 5, 115, 0, 0, 782, 783, 5, 116, 0, 0, 783, 784, 5, 101, 0, 0, 784, 785, 5, 114, 0, 0, 785, 140, 1, 0, 0, 0, 786, 787, 5, 99, 0, 0, 787, 788, 5, 114, 0, 0, 788, 789, 5, 100, 0, 0, 789, 790, 5, 116, 0, 0, 790, 142, 1, 0, 0, 0, 791, 792, 5, 111, 0, 0, 792, 793, 5, 112, 0, 0, 793, 794, 5, 116, 0, 0, 794, 795, 5, 105, 0, 0, 795, 796, 5, 111, 0, 0, 796, 797, 5, 110, 0, 0, 797, 798, 5, 97, 0, 0, 798, 799, 5, 108, 0, 0, 799, 800, 5, 45, 0, 0, 800, 801, 5, 111, 0, 0, 801, 802, 5, 110, 0, 0, 802, 803, 5, 101, 0, 0, 803, 144, 1, 0, 0, 0, 804, 805, 5, 101, 0, 0, 805, 806, 5, 120, 0, 0, 806, 807, 5, 97, 0, 0, 807, 808, 5, 99, 0, 0, 808, 809, 5, 116, 0, 0, 809, 810, 5, 108, 0, 0, 810, 811, 5, 121, 0, 0, 811, 812, 5, 45, 0, 0, 812, 813, 5, 111, 0, 0, 813, 814, 5, 110, 0, 0, 814, 815, 5, 101, 0, 0, 815, 146, 1, 0, 0, 0, 816, 817, 5, 109, 0, 0, 817, 818, 5, 97, 0, 0, 818, 819, 5, 110, 0, 0, 819, 820, 5, 121, 0, 0, 820, 821, 5, 45, 0, 0, 821, 822, 5, 117, 0, 0, 822, 823, 5, 110, 0, 0, 823, 824, 5, 105, 0, 0, 824, 825, 5, 113, 0, 0, 825, 826, 5, 117, 0, 0, 826, 827, 5, 101, 0, 0, 827, 148, 1, 0, 0, 0, 828, 829, 5, 109, 0, 0, 829, 830, 5, 97, 0, 0, 830, 831, 5, 110, 0, 0, 831, 832, 5, 121, 0, 0, 832, 150, 1, 0, 0, 0, 833, 834, 5, 111, 0, 0, 834, 835, 5, 114, 0, 0, 835, 836, 5, 100, 0, 0, 836, 837, 5, 101, 0, 0, 837, 838, 5, 114, 0, 0, 838, 839, 5, 101, 0, 0, 839, 840, 5, 100, 0, 0, 840, 152, 1, 0, 0, 0, 841, 842, 5, 117, 0, 0, 842, 843, 5, 110, 0, 0, 843, 844, 5, 105, 0, 0, 844, 845, 5, 116, 0, 0, 845, 154, 1, 0, 0, 0, 846, 847, 5, 119, 0, 0, 847, 848, 5, 97, 0, 0, 848, 849, 5, 116, 0, 0, 849, 850, 5, 99, 0, 0, 850, 851, 5, 104, 0, 0, 851, 852, 5, 45, 0, 0, 852, 853, 5, 104, 0, 0, 853, 854, 5, 97, 0, 0, 854, 855, 5, 110, 0, 0, 855, 856, 5, 100, 0, 0, 856, 857, 5, 108, 0, 0, 857, 858, 5, 101, 0, 0, 858, 156, 1, 0, 0, 0, 859, 860, 5, 109, 0, 0, 860, 861, 5, 101, 0, 0, 861, 862, 5, 115, 0, 0, 862, 863, 5, 115, 0, 0, 863, 864, 5, 97, 0, 0, 864, 865, 5, 103, 0, 0, 865, 866, 5, 101, 0, 0, 866, 158, 1, 0, 0, 0, 867, 868, 5, 97, 0, 0, 868, 869, 5, 116, 0, 0, 869, 870, 5, 111, 0, 0, 870, 871, 5, 109, 0, 0, 871, 872, 5, 45, 0, 0, 872, 873, 5, 114, 0, 0, 873, 874, 5, 101, 0, 0, 874, 875, 5, 102, 0, 0, 875, 160, 1, 0, 0, 0, 876, 877, 5, 105, 0, 0, 877, 878, 5, 110, 0, 0, 878, 879, 5, 116, 0, 0, 879, 880, 5, 101, 0, 0, 880, 881, 5, 114, 0, 0, 881, 882, 5, 102, 0, 0, 882, 883, 5, 97, 0, 0, 883, 884, 5, 99, 0, 0, 884, 885, 5, 101, 0, 0, 885, 886, 5, 45, 0, 0, 886, 887, 5, 114, 0, 0, 887, 888, 5, 101, 0, 0, 888, 889, 5, 102, 0, 0, 889, 162, 1, 0, 0, 0, 890, 891, 5, 111, 0, 0, 891, 892, 5, 112, 0, 0, 892, 893, 5, 116, 0, 0, 893, 894, 5, 105, 0, 0, 894, 895, 5, 111, 0, 0, 895, 896, 5, 110, 0, 0, 896, 897, 5, 97, 0, 0, 897, 898, 5, 108, 0, 0, 898, 164, 1, 0, 0, 0, 899, 900, 5, 108, 0, 0, 900, 901, 5, 105, 0, 0, 901, 902, 5, 115, 0, 0, 902, 903, 5, 116, 0, 0, 903, 166, 1, 0, 0, 0, 904, 905, 5, 114, 0, 0, 905, 906, 5, 101, 0, 0, 906, 907, 5, 99, 0, 0, 907, 908, 5, 111, 0, 0, 908, 909, 5, 114, 0, 0, 909, 910, 5, 100, 0, 0, 910, 168, 1, 0, 0, 0, 911, 912, 5, 98, 0, 0, 912, 913, 5, 111, 0, 0, 913, 914, 5, 111, 0, 0, 914, 915, 5, 108, 0, 0, 915, 170, 1, 0, 0, 0, 916, 917, 5, 98, 0, 0, 917, 918, 5, 121, 0, 0, 918, 919, 5, 116, 0, 0, 919, 920, 5, 101, 0, 0, 920, 921, 5, 115, 0, 0, 921, 172, 1, 0, 0, 0, 922, 923, 5, 100, 0, 0, 923, 924, 5, 111, 0, 0, 924, 925, 5, 117, 0, 0, 925, 926, 5, 98, 0, 0, 926, 927, 5, 108, 0, 0, 927, 928, 5, 101, 0, 0, 928, 174, 1, 0, 0, 0, 929, 930, 5, 105, 0, 0, 930, 931, 5, 110, 0, 0, 931, 932, 5, 116, 0, 0, 932, 933, 5, 51, 0, 0, 933, 934, 5, 50, 0, 0, 934, 176, 1, 0, 0, 0, 935, 936, 5, 105, 0, 0, 936, 937, 5, 110, 0, 0, 937, 938, 5, 116, 0, 0, 938, 939, 5, 54, 0, 0, 939, 940, 5, 52, 0, 0, 940, 178, 1, 0, 0, 0, 941, 942, 5, 115, 0, 0, 942, 943, 5, 116, 0, 0, 943, 944, 5, 114, 0, 0, 944, 945, 5, 105, 0, 0, 945, 946, 5, 110, 0, 0, 946, 947, 5, 103, 0, 0, 947, 180, 1, 0, 0, 0, 948, 949, 5, 117, 0, 0, 949, 950, 5, 105, 0, 0, 950, 951, 5, 110, 0, 0, 951, 952, 5, 116, 0, 0, 952, 953, 5, 51, 0, 0, 953, 954, 5, 50, 0, 0, 954, 182, 1, 0, 0, 0, 955, 956, 5, 117, 0, 0, 956, 957, 5, 105, 0, 0, 957, 958, 5, 110, 0, 0, 958, 959, 5, 116, 0, 0, 959, 960, 5, 54, 0, 0, 960, 961, 5, 52, 0, 0, 961, 184, 1, 0, 0, 0, 962, 963, 5, 116, 0, 0, 963, 964, 5, 114, 0, 0, 964, 965, 5, 117, 0, 0, 965, 966, 5, 101, 0, 0, 966, 186, 1, 0, 0, 0, 967, 968, 5, 102, 0, 0, 968, 969, 5, 97, 0, 0, 969, 970, 5, 108, 0, 0, 970, 971, 5, 115, 0, 0, 971, 972, 5, 101, 0, 0, 972, 188, 1, 0, 0, 0, 973, 974, 5, 110, 0, 0, 974, 975, 5, 117, 0, 0, 975, 976, 5, 108, 0, 0, 976, 977, 5, 108, 0, 0, 977, 190, 1, 0, 0, 0, 978, 979, 5, 45, 0, 0, 979, 980, 5, 62, 0, 0, 980, 192, 1, 0, 0, 0, 981, 982, 5, 58, 0, 0, 982, 194, 1, 0, 0, 0, 983, 984, 5, 59, 0, 0, 984, 196, 1, 0, 0, 0, 985, 986, 5, 44, 0, 0, 986, 198, 1, 0, 0, 0, 987, 988, 5, 46, 0, 0, 988, 200, 1, 0, 0, 0, 989, 990, 5, 123, 0, 0, 990, 202, 1, 0, 0, 0, 991, 992, 5, 125, 0, 0, 992, 204, 1, 0, 0, 0, 993, 994, 5, 91, 0, 0, 994, 206, 1, 0, 0, 0, 995, 996, 5, 93, 0, 0, 996, 208, 1, 0, 0, 0, 997, 998, 5, 40, 0, 0, 998, 210, 1, 0, 0, 0, 999, 1000, 5, 41, 0, 0, 1000, 212, 1, 0, 0, 0, 1001, 1002, 5, 60, 0, 0, 1002, 214, 1, 0, 0, 0, 1003, 1004, 5, 62, 0, 0, 1004, 216, 1, 0, 0, 0, 1005, 1006, 5, 38, 0, 0, 1006, 218, 1, 0, 0, 0, 1007, 1008, 5, 61, 0, 0, 1008, 220, 1, 0, 0, 0, 1009, 1011, 5, 45, 0, 0, 1010, 1009, 1, 0, 0, 0, 1010, 1011, 1, 0, 0, 0, 1011, 1013, 1, 0, 0, 0, 1012, 1014, 7, 0, 0, 0, 1013, 1012, 1, 0, 0, 0, 1014, 1015, 1, 0, 0, 0, 1015, 1013, 1, 0, 0, 0, 1015, 1016, 1, 0, 0, 0, 1016, 222, 1, 0, 0, 0, 1017, 1019, 5, 45, 0, 0, 1018, 1017, 1, 0, 0, 0, 1018, 1019, 1, 0, 0, 0, 1019, 1028, 1, 0, 0, 0, 1020, 1029, 5, 48, 0, 0, 1021, 1025, 7, 1, 0, 0, 1022, 1024, 7, 0, 0, 0, 1023, 1022, 1, 0, 0, 0, 1024, 1027, 1, 0, 0, 0, 1025, 1023, 1, 0, 0, 0, 1025, 1026, 1, 0, 0, 0, 1026, 1029, 1, 0, 0, 0, 1027, 1025, 1, 0, 0, 0, 1028, 1020, 1, 0, 0, 0, 1028, 1021, 1, 0, 0, 0, 1029, 1036, 1, 0, 0, 0, 1030, 1032, 5, 46, 0, 0, 1031, 1033, 7, 0, 0, 0, 1032, 1031, 1, 0, 0, 0, 1033, 1034, 1, 0, 0, 0, 1034, 1032, 1, 0, 0, 0, 1034, 1035, 1, 0, 0, 0, 1035, 1037, 1, 0, 0, 0, 1036, 1030, 1, 0, 0, 0, 1036, 1037, 1, 0, 0, 0, 1037, 1047, 1, 0, 0, 0, 1038, 1040, 7, 2, 0, 0, 1039, 1041, 7, 3, 0, 0, 1040, 1039, 1, 0, 0, 0, 1040, 1041, 1, 0, 0, 0, 1041, 1043, 1, 0, 0, 0, 1042, 1044, 7, 0, 0, 0, 1043, 1042, 1, 0, 0, 0, 1044, 1045, 1, 0, 0, 0, 1045, 1043, 1, 0, 0, 0, 1045, 1046, 1, 0, 0, 0, 1046, 1048, 1, 0, 0, 0, 1047, 1038, 1, 0, 0, 0, 1047, 1048, 1, 0, 0, 0, 1048, 224, 1, 0, 0, 0, 1049, 1053, 7, 4, 0, 0, 1050, 1052, 7, 5, 0, 0, 1051, 1050, 1, 0, 0, 0, 1052, 1055, 1, 0, 0, 0, 1053, 1051, 1, 0, 0, 0, 1053, 1054, 1, 0, 0, 0, 1054, 226, 1, 0, 0, 0, 1055, 1053, 1, 0, 0, 0, 1056, 1061, 5, 34, 0, 0, 1057, 1060, 3, 229, 114, 0, 1058, 1060, 8, 6, 0, 0, 1059, 1057, 1, 0, 0, 0, 1059, 1058, 1, 0, 0, 0, 1060, 1063, 1, 0, 0, 0, 1061, 1059, 1, 0, 0, 0, 1061, 1062, 1, 0, 0, 0, 1062, 1064, 1, 0, 0, 0, 1063, 1061, 1, 0, 0, 0, 1064, 1065, 5, 34, 0, 0, 1065, 228, 1, 0, 0, 0, 1066, 1074, 5, 92, 0, 0, 1067, 1075, 7, 7, 0, 0, 1068, 1069, 5, 117, 0, 0, 1069, 1070, 3, 231, 115, 0, 1070, 1071, 3, 231, 115, 0, 1071, 1072, 3, 231, 115, 0, 1072, 1073, 3, 231, 115, 0, 1073, 1075, 1, 0, 0, 0, 1074, 1067, 1, 0, 0, 0, 1074, 1068, 1, 0, 0, 0, 1075, 230, 1, 0, 0, 0, 1076, 1077, 7, 8, 0, 0, 1077, 232, 1, 0, 0, 0, 1078, 1079, 5, 47, 0, 0, 1079, 1080, 5, 47, 0, 0, 1080, 1084, 1, 0, 0, 0, 1081, 1083, 8, 9, 0, 0, 1082, 1081, 1, 0, 0, 0, 1083, 1086, 1, 0, 0, 0, 1084, 1082, 1, 0, 0, 0, 1084, 1085, 1, 0, 0, 0, 1085, 1087, 1, 0, 0, 0, 1086, 1084, 1, 0, 0, 0, 1087, 1088, 6, 116, 0, 0, 1088, 234, 1, 0, 0, 0, 1089, 1090, 5, 47, 0, 0, 1090, 1091, 5, 42, 0, 0, 1091, 1095, 1, 0, 0, 0, 1092, 1094, 9, 0, 0, 0, 1093, 1092, 1, 0, 0, 0, 1094, 1097, 1, 0, 0, 0, 1095, 1096, 1, 0, 0, 0, 1095, 1093, 1, 0, 0, 0, 1096, 1098, 1, 0, 0, 0, 1097, 1095, 1, 0, 0, 0, 1098, 1099, 5, 42, 0, 0, 1099, 1100, 5, 47, 0, 0, 1100, 1101, 1, 0, 0, 0, 1101, 1102, 6, 117, 0, 0, 1102, 236, 1, 0, 0, 0, 1103, 1105, 7, 10, 0, 0, 1104, 1103, 1, 0, 0, 0, 1105, 1106, 1, 0, 0, 0, 1106, 1104, 1, 0, 0, 0, 1106, 1107, 1, 0, 0, 0, 1107, 1108, 1, 0, 0, 0, 1108, 1109, 6, 118, 0, 0, 1109, 238, 1, 0, 0, 0, 18, 0, 1010, 1015, 1018, 1025, 1028, 1034, 1036, 1040, 1045, 1047, 1053, 1059, 1061, 1074, 1084, 1095, 1106, 1, 0, 1, 0] \ No newline at end of file diff --git a/src/capability-language/generated/QuixosCapabilityLexer.tokens b/src/capability-language/generated/QuixosCapabilityLexer.tokens index 8571c0b..75f42fb 100644 --- a/src/capability-language/generated/QuixosCapabilityLexer.tokens +++ b/src/capability-language/generated/QuixosCapabilityLexer.tokens @@ -1,213 +1,227 @@ WORKSPACE=1 -FRAGMENT=2 -IMPORT=3 -EXTERNAL=4 -ATOM=5 -INTERFACE=6 -INTERFACES=7 -PACKAGE=8 -VALUE=9 -RELATION=10 -OPERATION=11 -FUNCTION=12 -CONSTRUCTOR=13 -CONSTRUCTS=14 -INPUT=15 -CONFORM=16 -AS=17 -BIND=18 -TO=19 -PRIVATE=20 -SHARED=21 -STATE=22 -EDGE=23 -PROJECTION=24 -WITH=25 -USING=26 -VIA=27 -MATERIALIZE=28 -IF=29 -ABSENT=30 -ON=31 -POLICY=32 -DEFAULT=33 -SOURCE=34 -REPOSITORY=35 -COMMIT=36 -REVISION=37 -SEMANTIC_MAJOR=38 -ON_DELETE=39 -RETAIN_OTHER=40 -KEYED=41 -PUBLIC_TRAVERSAL=42 -ID=43 -DOC=44 -MODE=45 -EMITS=46 -RECEIVER=47 -REQUIRES=48 -ANY=49 -GET=50 -SET=51 -WATCH=52 -START=53 -STOP=54 -READ=55 -WRITE=56 -RESOLVE=57 -CONNECT=58 -DISCONNECT=59 -CALL=60 -WATCH_START=61 -WATCH_STOP=62 -SUBSCRIBE=63 -UNSUBSCRIBE=64 -OPTIMISTIC_REGISTER=65 -CRDT=66 -OPTIONAL_ONE=67 -EXACTLY_ONE=68 -MANY_UNIQUE=69 -MANY=70 -ORDERED=71 -UNIT=72 -WATCH_HANDLE=73 -MESSAGE=74 -ATOM_REF=75 -INTERFACE_REF=76 -OPTIONAL=77 -LIST=78 -RECORD=79 -BOOL=80 -BYTES=81 -DOUBLE=82 -INT32=83 -INT64=84 -STRING=85 -UINT32=86 -UINT64=87 -TRUE=88 -FALSE=89 -NULL=90 -ARROW=91 -COLON=92 -SEMI=93 -COMMA=94 -DOT=95 -LBRACE=96 -RBRACE=97 -LBRACK=98 -RBRACK=99 -LPAREN=100 -RPAREN=101 -LT=102 -GT=103 -INTEGER=104 -JSON_NUMBER=105 -IDENTIFIER=106 -STRING_LITERAL=107 -LINE_COMMENT=108 -BLOCK_COMMENT=109 -WS=110 +TYPE=2 +OBJECT=3 +STORABLE=4 +IMPLEMENTS=5 +REF=6 +FRAGMENT=7 +IMPORT=8 +EXTERNAL=9 +ATOM=10 +INTERFACE=11 +INTERFACES=12 +PACKAGE=13 +VALUE=14 +RELATION=15 +OPERATION=16 +FUNCTION=17 +CONSTRUCTOR=18 +CONSTRUCTS=19 +INPUT=20 +CONFORM=21 +AS=22 +BIND=23 +TO=24 +PRIVATE=25 +SHARED=26 +STATE=27 +EDGE=28 +PROJECTION=29 +WITH=30 +USING=31 +VIA=32 +MATERIALIZE=33 +IF=34 +ABSENT=35 +ON=36 +POLICY=37 +DEFAULT=38 +SOURCE=39 +REPOSITORY=40 +COMMIT=41 +REVISION=42 +SEMANTIC_MAJOR=43 +ON_DELETE=44 +RETAIN_OTHER=45 +KEYED=46 +PUBLIC_TRAVERSAL=47 +ID=48 +DOC=49 +MODE=50 +EMITS=51 +RECEIVER=52 +REQUIRES=53 +ANY=54 +GET=55 +SET=56 +WATCH=57 +START=58 +STOP=59 +READ=60 +WRITE=61 +RESOLVE=62 +CONNECT=63 +DISCONNECT=64 +CALL=65 +WATCH_START=66 +WATCH_STOP=67 +SUBSCRIBE=68 +UNSUBSCRIBE=69 +OPTIMISTIC_REGISTER=70 +CRDT=71 +OPTIONAL_ONE=72 +EXACTLY_ONE=73 +MANY_UNIQUE=74 +MANY=75 +ORDERED=76 +UNIT=77 +WATCH_HANDLE=78 +MESSAGE=79 +ATOM_REF=80 +INTERFACE_REF=81 +OPTIONAL=82 +LIST=83 +RECORD=84 +BOOL=85 +BYTES=86 +DOUBLE=87 +INT32=88 +INT64=89 +STRING=90 +UINT32=91 +UINT64=92 +TRUE=93 +FALSE=94 +NULL=95 +ARROW=96 +COLON=97 +SEMI=98 +COMMA=99 +DOT=100 +LBRACE=101 +RBRACE=102 +LBRACK=103 +RBRACK=104 +LPAREN=105 +RPAREN=106 +LT=107 +GT=108 +AMP=109 +EQUAL=110 +INTEGER=111 +JSON_NUMBER=112 +IDENTIFIER=113 +STRING_LITERAL=114 +LINE_COMMENT=115 +BLOCK_COMMENT=116 +WS=117 'workspace'=1 -'fragment'=2 -'import'=3 -'external'=4 -'atom'=5 -'interface'=6 -'interfaces'=7 -'package'=8 -'value'=9 -'relation'=10 -'operation'=11 -'function'=12 -'constructor'=13 -'constructs'=14 -'input'=15 -'conform'=16 -'as'=17 -'bind'=18 -'to'=19 -'private'=20 -'shared'=21 -'state'=22 -'edge'=23 -'projection'=24 -'with'=25 -'using'=26 -'via'=27 -'materialize'=28 -'if'=29 -'absent'=30 -'on'=31 -'policy'=32 -'default'=33 -'source'=34 -'repository'=35 -'commit'=36 -'revision'=37 -'semantic-major'=38 -'on-delete'=39 -'retain-other'=40 -'keyed'=41 -'public-traversal'=42 -'id'=43 -'doc'=44 -'mode'=45 -'emits'=46 -'receiver'=47 -'requires'=48 -'any'=49 -'get'=50 -'set'=51 -'watch'=52 -'start'=53 -'stop'=54 -'read'=55 -'write'=56 -'resolve'=57 -'connect'=58 -'disconnect'=59 -'call'=60 -'watch-start'=61 -'watch-stop'=62 -'subscribe'=63 -'unsubscribe'=64 -'optimistic-register'=65 -'crdt'=66 -'optional-one'=67 -'exactly-one'=68 -'many-unique'=69 -'many'=70 -'ordered'=71 -'unit'=72 -'watch-handle'=73 -'message'=74 -'atom-ref'=75 -'interface-ref'=76 -'optional'=77 -'list'=78 -'record'=79 -'bool'=80 -'bytes'=81 -'double'=82 -'int32'=83 -'int64'=84 -'string'=85 -'uint32'=86 -'uint64'=87 -'true'=88 -'false'=89 -'null'=90 -'->'=91 -':'=92 -';'=93 -','=94 -'.'=95 -'{'=96 -'}'=97 -'['=98 -']'=99 -'('=100 -')'=101 -'<'=102 -'>'=103 +'type'=2 +'object'=3 +'storable'=4 +'implements'=5 +'ref'=6 +'fragment'=7 +'import'=8 +'external'=9 +'atom'=10 +'interface'=11 +'interfaces'=12 +'package'=13 +'value'=14 +'relation'=15 +'operation'=16 +'function'=17 +'constructor'=18 +'constructs'=19 +'input'=20 +'conform'=21 +'as'=22 +'bind'=23 +'to'=24 +'private'=25 +'shared'=26 +'state'=27 +'edge'=28 +'projection'=29 +'with'=30 +'using'=31 +'via'=32 +'materialize'=33 +'if'=34 +'absent'=35 +'on'=36 +'policy'=37 +'default'=38 +'source'=39 +'repository'=40 +'commit'=41 +'revision'=42 +'semantic-major'=43 +'on-delete'=44 +'retain-other'=45 +'keyed'=46 +'public-traversal'=47 +'id'=48 +'doc'=49 +'mode'=50 +'emits'=51 +'receiver'=52 +'requires'=53 +'any'=54 +'get'=55 +'set'=56 +'watch'=57 +'start'=58 +'stop'=59 +'read'=60 +'write'=61 +'resolve'=62 +'connect'=63 +'disconnect'=64 +'call'=65 +'watch-start'=66 +'watch-stop'=67 +'subscribe'=68 +'unsubscribe'=69 +'optimistic-register'=70 +'crdt'=71 +'optional-one'=72 +'exactly-one'=73 +'many-unique'=74 +'many'=75 +'ordered'=76 +'unit'=77 +'watch-handle'=78 +'message'=79 +'atom-ref'=80 +'interface-ref'=81 +'optional'=82 +'list'=83 +'record'=84 +'bool'=85 +'bytes'=86 +'double'=87 +'int32'=88 +'int64'=89 +'string'=90 +'uint32'=91 +'uint64'=92 +'true'=93 +'false'=94 +'null'=95 +'->'=96 +':'=97 +';'=98 +','=99 +'.'=100 +'{'=101 +'}'=102 +'['=103 +']'=104 +'('=105 +')'=106 +'<'=107 +'>'=108 +'&'=109 +'='=110 diff --git a/src/capability-language/generated/QuixosCapabilityLexer.ts b/src/capability-language/generated/QuixosCapabilityLexer.ts index 21de55e..0aa0f03 100644 --- a/src/capability-language/generated/QuixosCapabilityLexer.ts +++ b/src/capability-language/generated/QuixosCapabilityLexer.ts @@ -5,160 +5,169 @@ import { Token } from "antlr4ng"; export class QuixosCapabilityLexer extends antlr.Lexer { public static readonly WORKSPACE = 1; - public static readonly FRAGMENT = 2; - public static readonly IMPORT = 3; - public static readonly EXTERNAL = 4; - public static readonly ATOM = 5; - public static readonly INTERFACE = 6; - public static readonly INTERFACES = 7; - public static readonly PACKAGE = 8; - public static readonly VALUE = 9; - public static readonly RELATION = 10; - public static readonly OPERATION = 11; - public static readonly FUNCTION = 12; - public static readonly CONSTRUCTOR = 13; - public static readonly CONSTRUCTS = 14; - public static readonly INPUT = 15; - public static readonly CONFORM = 16; - public static readonly AS = 17; - public static readonly BIND = 18; - public static readonly TO = 19; - public static readonly PRIVATE = 20; - public static readonly SHARED = 21; - public static readonly STATE = 22; - public static readonly EDGE = 23; - public static readonly PROJECTION = 24; - public static readonly WITH = 25; - public static readonly USING = 26; - public static readonly VIA = 27; - public static readonly MATERIALIZE = 28; - public static readonly IF = 29; - public static readonly ABSENT = 30; - public static readonly ON = 31; - public static readonly POLICY = 32; - public static readonly DEFAULT = 33; - public static readonly SOURCE = 34; - public static readonly REPOSITORY = 35; - public static readonly COMMIT = 36; - public static readonly REVISION = 37; - public static readonly SEMANTIC_MAJOR = 38; - public static readonly ON_DELETE = 39; - public static readonly RETAIN_OTHER = 40; - public static readonly KEYED = 41; - public static readonly PUBLIC_TRAVERSAL = 42; - public static readonly ID = 43; - public static readonly DOC = 44; - public static readonly MODE = 45; - public static readonly EMITS = 46; - public static readonly RECEIVER = 47; - public static readonly REQUIRES = 48; - public static readonly ANY = 49; - public static readonly GET = 50; - public static readonly SET = 51; - public static readonly WATCH = 52; - public static readonly START = 53; - public static readonly STOP = 54; - public static readonly READ = 55; - public static readonly WRITE = 56; - public static readonly RESOLVE = 57; - public static readonly CONNECT = 58; - public static readonly DISCONNECT = 59; - public static readonly CALL = 60; - public static readonly WATCH_START = 61; - public static readonly WATCH_STOP = 62; - public static readonly SUBSCRIBE = 63; - public static readonly UNSUBSCRIBE = 64; - public static readonly OPTIMISTIC_REGISTER = 65; - public static readonly CRDT = 66; - public static readonly OPTIONAL_ONE = 67; - public static readonly EXACTLY_ONE = 68; - public static readonly MANY_UNIQUE = 69; - public static readonly MANY = 70; - public static readonly ORDERED = 71; - public static readonly UNIT = 72; - public static readonly WATCH_HANDLE = 73; - public static readonly MESSAGE = 74; - public static readonly ATOM_REF = 75; - public static readonly INTERFACE_REF = 76; - public static readonly OPTIONAL = 77; - public static readonly LIST = 78; - public static readonly RECORD = 79; - public static readonly BOOL = 80; - public static readonly BYTES = 81; - public static readonly DOUBLE = 82; - public static readonly INT32 = 83; - public static readonly INT64 = 84; - public static readonly STRING = 85; - public static readonly UINT32 = 86; - public static readonly UINT64 = 87; - public static readonly TRUE = 88; - public static readonly FALSE = 89; - public static readonly NULL = 90; - public static readonly ARROW = 91; - public static readonly COLON = 92; - public static readonly SEMI = 93; - public static readonly COMMA = 94; - public static readonly DOT = 95; - public static readonly LBRACE = 96; - public static readonly RBRACE = 97; - public static readonly LBRACK = 98; - public static readonly RBRACK = 99; - public static readonly LPAREN = 100; - public static readonly RPAREN = 101; - public static readonly LT = 102; - public static readonly GT = 103; - public static readonly INTEGER = 104; - public static readonly JSON_NUMBER = 105; - public static readonly IDENTIFIER = 106; - public static readonly STRING_LITERAL = 107; - public static readonly LINE_COMMENT = 108; - public static readonly BLOCK_COMMENT = 109; - public static readonly WS = 110; + public static readonly TYPE = 2; + public static readonly OBJECT = 3; + public static readonly STORABLE = 4; + public static readonly IMPLEMENTS = 5; + public static readonly REF = 6; + public static readonly FRAGMENT = 7; + public static readonly IMPORT = 8; + public static readonly EXTERNAL = 9; + public static readonly ATOM = 10; + public static readonly INTERFACE = 11; + public static readonly INTERFACES = 12; + public static readonly PACKAGE = 13; + public static readonly VALUE = 14; + public static readonly RELATION = 15; + public static readonly OPERATION = 16; + public static readonly FUNCTION = 17; + public static readonly CONSTRUCTOR = 18; + public static readonly CONSTRUCTS = 19; + public static readonly INPUT = 20; + public static readonly CONFORM = 21; + public static readonly AS = 22; + public static readonly BIND = 23; + public static readonly TO = 24; + public static readonly PRIVATE = 25; + public static readonly SHARED = 26; + public static readonly STATE = 27; + public static readonly EDGE = 28; + public static readonly PROJECTION = 29; + public static readonly WITH = 30; + public static readonly USING = 31; + public static readonly VIA = 32; + public static readonly MATERIALIZE = 33; + public static readonly IF = 34; + public static readonly ABSENT = 35; + public static readonly ON = 36; + public static readonly POLICY = 37; + public static readonly DEFAULT = 38; + public static readonly SOURCE = 39; + public static readonly REPOSITORY = 40; + public static readonly COMMIT = 41; + public static readonly REVISION = 42; + public static readonly SEMANTIC_MAJOR = 43; + public static readonly ON_DELETE = 44; + public static readonly RETAIN_OTHER = 45; + public static readonly KEYED = 46; + public static readonly PUBLIC_TRAVERSAL = 47; + public static readonly ID = 48; + public static readonly DOC = 49; + public static readonly MODE = 50; + public static readonly EMITS = 51; + public static readonly RECEIVER = 52; + public static readonly REQUIRES = 53; + public static readonly ANY = 54; + public static readonly GET = 55; + public static readonly SET = 56; + public static readonly WATCH = 57; + public static readonly START = 58; + public static readonly STOP = 59; + public static readonly READ = 60; + public static readonly WRITE = 61; + public static readonly RESOLVE = 62; + public static readonly CONNECT = 63; + public static readonly DISCONNECT = 64; + public static readonly CALL = 65; + public static readonly WATCH_START = 66; + public static readonly WATCH_STOP = 67; + public static readonly SUBSCRIBE = 68; + public static readonly UNSUBSCRIBE = 69; + public static readonly OPTIMISTIC_REGISTER = 70; + public static readonly CRDT = 71; + public static readonly OPTIONAL_ONE = 72; + public static readonly EXACTLY_ONE = 73; + public static readonly MANY_UNIQUE = 74; + public static readonly MANY = 75; + public static readonly ORDERED = 76; + public static readonly UNIT = 77; + public static readonly WATCH_HANDLE = 78; + public static readonly MESSAGE = 79; + public static readonly ATOM_REF = 80; + public static readonly INTERFACE_REF = 81; + public static readonly OPTIONAL = 82; + public static readonly LIST = 83; + public static readonly RECORD = 84; + public static readonly BOOL = 85; + public static readonly BYTES = 86; + public static readonly DOUBLE = 87; + public static readonly INT32 = 88; + public static readonly INT64 = 89; + public static readonly STRING = 90; + public static readonly UINT32 = 91; + public static readonly UINT64 = 92; + public static readonly TRUE = 93; + public static readonly FALSE = 94; + public static readonly NULL = 95; + public static readonly ARROW = 96; + public static readonly COLON = 97; + public static readonly SEMI = 98; + public static readonly COMMA = 99; + public static readonly DOT = 100; + public static readonly LBRACE = 101; + public static readonly RBRACE = 102; + public static readonly LBRACK = 103; + public static readonly RBRACK = 104; + public static readonly LPAREN = 105; + public static readonly RPAREN = 106; + public static readonly LT = 107; + public static readonly GT = 108; + public static readonly AMP = 109; + public static readonly EQUAL = 110; + public static readonly INTEGER = 111; + public static readonly JSON_NUMBER = 112; + public static readonly IDENTIFIER = 113; + public static readonly STRING_LITERAL = 114; + public static readonly LINE_COMMENT = 115; + public static readonly BLOCK_COMMENT = 116; + public static readonly WS = 117; public static readonly channelNames = [ "DEFAULT_TOKEN_CHANNEL", "HIDDEN" ]; public static readonly literalNames = [ - null, "'workspace'", "'fragment'", "'import'", "'external'", "'atom'", - "'interface'", "'interfaces'", "'package'", "'value'", "'relation'", - "'operation'", "'function'", "'constructor'", "'constructs'", "'input'", - "'conform'", "'as'", "'bind'", "'to'", "'private'", "'shared'", - "'state'", "'edge'", "'projection'", "'with'", "'using'", "'via'", - "'materialize'", "'if'", "'absent'", "'on'", "'policy'", "'default'", - "'source'", "'repository'", "'commit'", "'revision'", "'semantic-major'", - "'on-delete'", "'retain-other'", "'keyed'", "'public-traversal'", - "'id'", "'doc'", "'mode'", "'emits'", "'receiver'", "'requires'", - "'any'", "'get'", "'set'", "'watch'", "'start'", "'stop'", "'read'", - "'write'", "'resolve'", "'connect'", "'disconnect'", "'call'", "'watch-start'", - "'watch-stop'", "'subscribe'", "'unsubscribe'", "'optimistic-register'", - "'crdt'", "'optional-one'", "'exactly-one'", "'many-unique'", "'many'", - "'ordered'", "'unit'", "'watch-handle'", "'message'", "'atom-ref'", - "'interface-ref'", "'optional'", "'list'", "'record'", "'bool'", - "'bytes'", "'double'", "'int32'", "'int64'", "'string'", "'uint32'", - "'uint64'", "'true'", "'false'", "'null'", "'->'", "':'", "';'", - "','", "'.'", "'{'", "'}'", "'['", "']'", "'('", "')'", "'<'", "'>'" + null, "'workspace'", "'type'", "'object'", "'storable'", "'implements'", + "'ref'", "'fragment'", "'import'", "'external'", "'atom'", "'interface'", + "'interfaces'", "'package'", "'value'", "'relation'", "'operation'", + "'function'", "'constructor'", "'constructs'", "'input'", "'conform'", + "'as'", "'bind'", "'to'", "'private'", "'shared'", "'state'", "'edge'", + "'projection'", "'with'", "'using'", "'via'", "'materialize'", "'if'", + "'absent'", "'on'", "'policy'", "'default'", "'source'", "'repository'", + "'commit'", "'revision'", "'semantic-major'", "'on-delete'", "'retain-other'", + "'keyed'", "'public-traversal'", "'id'", "'doc'", "'mode'", "'emits'", + "'receiver'", "'requires'", "'any'", "'get'", "'set'", "'watch'", + "'start'", "'stop'", "'read'", "'write'", "'resolve'", "'connect'", + "'disconnect'", "'call'", "'watch-start'", "'watch-stop'", "'subscribe'", + "'unsubscribe'", "'optimistic-register'", "'crdt'", "'optional-one'", + "'exactly-one'", "'many-unique'", "'many'", "'ordered'", "'unit'", + "'watch-handle'", "'message'", "'atom-ref'", "'interface-ref'", + "'optional'", "'list'", "'record'", "'bool'", "'bytes'", "'double'", + "'int32'", "'int64'", "'string'", "'uint32'", "'uint64'", "'true'", + "'false'", "'null'", "'->'", "':'", "';'", "','", "'.'", "'{'", + "'}'", "'['", "']'", "'('", "')'", "'<'", "'>'", "'&'", "'='" ]; public static readonly symbolicNames = [ - null, "WORKSPACE", "FRAGMENT", "IMPORT", "EXTERNAL", "ATOM", "INTERFACE", - "INTERFACES", "PACKAGE", "VALUE", "RELATION", "OPERATION", "FUNCTION", - "CONSTRUCTOR", "CONSTRUCTS", "INPUT", "CONFORM", "AS", "BIND", "TO", - "PRIVATE", "SHARED", "STATE", "EDGE", "PROJECTION", "WITH", "USING", - "VIA", "MATERIALIZE", "IF", "ABSENT", "ON", "POLICY", "DEFAULT", - "SOURCE", "REPOSITORY", "COMMIT", "REVISION", "SEMANTIC_MAJOR", - "ON_DELETE", "RETAIN_OTHER", "KEYED", "PUBLIC_TRAVERSAL", "ID", - "DOC", "MODE", "EMITS", "RECEIVER", "REQUIRES", "ANY", "GET", "SET", - "WATCH", "START", "STOP", "READ", "WRITE", "RESOLVE", "CONNECT", - "DISCONNECT", "CALL", "WATCH_START", "WATCH_STOP", "SUBSCRIBE", - "UNSUBSCRIBE", "OPTIMISTIC_REGISTER", "CRDT", "OPTIONAL_ONE", "EXACTLY_ONE", - "MANY_UNIQUE", "MANY", "ORDERED", "UNIT", "WATCH_HANDLE", "MESSAGE", - "ATOM_REF", "INTERFACE_REF", "OPTIONAL", "LIST", "RECORD", "BOOL", - "BYTES", "DOUBLE", "INT32", "INT64", "STRING", "UINT32", "UINT64", - "TRUE", "FALSE", "NULL", "ARROW", "COLON", "SEMI", "COMMA", "DOT", - "LBRACE", "RBRACE", "LBRACK", "RBRACK", "LPAREN", "RPAREN", "LT", - "GT", "INTEGER", "JSON_NUMBER", "IDENTIFIER", "STRING_LITERAL", - "LINE_COMMENT", "BLOCK_COMMENT", "WS" + null, "WORKSPACE", "TYPE", "OBJECT", "STORABLE", "IMPLEMENTS", "REF", + "FRAGMENT", "IMPORT", "EXTERNAL", "ATOM", "INTERFACE", "INTERFACES", + "PACKAGE", "VALUE", "RELATION", "OPERATION", "FUNCTION", "CONSTRUCTOR", + "CONSTRUCTS", "INPUT", "CONFORM", "AS", "BIND", "TO", "PRIVATE", + "SHARED", "STATE", "EDGE", "PROJECTION", "WITH", "USING", "VIA", + "MATERIALIZE", "IF", "ABSENT", "ON", "POLICY", "DEFAULT", "SOURCE", + "REPOSITORY", "COMMIT", "REVISION", "SEMANTIC_MAJOR", "ON_DELETE", + "RETAIN_OTHER", "KEYED", "PUBLIC_TRAVERSAL", "ID", "DOC", "MODE", + "EMITS", "RECEIVER", "REQUIRES", "ANY", "GET", "SET", "WATCH", "START", + "STOP", "READ", "WRITE", "RESOLVE", "CONNECT", "DISCONNECT", "CALL", + "WATCH_START", "WATCH_STOP", "SUBSCRIBE", "UNSUBSCRIBE", "OPTIMISTIC_REGISTER", + "CRDT", "OPTIONAL_ONE", "EXACTLY_ONE", "MANY_UNIQUE", "MANY", "ORDERED", + "UNIT", "WATCH_HANDLE", "MESSAGE", "ATOM_REF", "INTERFACE_REF", + "OPTIONAL", "LIST", "RECORD", "BOOL", "BYTES", "DOUBLE", "INT32", + "INT64", "STRING", "UINT32", "UINT64", "TRUE", "FALSE", "NULL", + "ARROW", "COLON", "SEMI", "COMMA", "DOT", "LBRACE", "RBRACE", "LBRACK", + "RBRACK", "LPAREN", "RPAREN", "LT", "GT", "AMP", "EQUAL", "INTEGER", + "JSON_NUMBER", "IDENTIFIER", "STRING_LITERAL", "LINE_COMMENT", "BLOCK_COMMENT", + "WS" ]; public static readonly modeNames = [ @@ -166,24 +175,25 @@ export class QuixosCapabilityLexer extends antlr.Lexer { ]; public static readonly ruleNames = [ - "WORKSPACE", "FRAGMENT", "IMPORT", "EXTERNAL", "ATOM", "INTERFACE", - "INTERFACES", "PACKAGE", "VALUE", "RELATION", "OPERATION", "FUNCTION", - "CONSTRUCTOR", "CONSTRUCTS", "INPUT", "CONFORM", "AS", "BIND", "TO", - "PRIVATE", "SHARED", "STATE", "EDGE", "PROJECTION", "WITH", "USING", - "VIA", "MATERIALIZE", "IF", "ABSENT", "ON", "POLICY", "DEFAULT", - "SOURCE", "REPOSITORY", "COMMIT", "REVISION", "SEMANTIC_MAJOR", - "ON_DELETE", "RETAIN_OTHER", "KEYED", "PUBLIC_TRAVERSAL", "ID", - "DOC", "MODE", "EMITS", "RECEIVER", "REQUIRES", "ANY", "GET", "SET", - "WATCH", "START", "STOP", "READ", "WRITE", "RESOLVE", "CONNECT", - "DISCONNECT", "CALL", "WATCH_START", "WATCH_STOP", "SUBSCRIBE", - "UNSUBSCRIBE", "OPTIMISTIC_REGISTER", "CRDT", "OPTIONAL_ONE", "EXACTLY_ONE", - "MANY_UNIQUE", "MANY", "ORDERED", "UNIT", "WATCH_HANDLE", "MESSAGE", - "ATOM_REF", "INTERFACE_REF", "OPTIONAL", "LIST", "RECORD", "BOOL", - "BYTES", "DOUBLE", "INT32", "INT64", "STRING", "UINT32", "UINT64", - "TRUE", "FALSE", "NULL", "ARROW", "COLON", "SEMI", "COMMA", "DOT", - "LBRACE", "RBRACE", "LBRACK", "RBRACK", "LPAREN", "RPAREN", "LT", - "GT", "INTEGER", "JSON_NUMBER", "IDENTIFIER", "STRING_LITERAL", - "ESC", "HEX", "LINE_COMMENT", "BLOCK_COMMENT", "WS", + "WORKSPACE", "TYPE", "OBJECT", "STORABLE", "IMPLEMENTS", "REF", + "FRAGMENT", "IMPORT", "EXTERNAL", "ATOM", "INTERFACE", "INTERFACES", + "PACKAGE", "VALUE", "RELATION", "OPERATION", "FUNCTION", "CONSTRUCTOR", + "CONSTRUCTS", "INPUT", "CONFORM", "AS", "BIND", "TO", "PRIVATE", + "SHARED", "STATE", "EDGE", "PROJECTION", "WITH", "USING", "VIA", + "MATERIALIZE", "IF", "ABSENT", "ON", "POLICY", "DEFAULT", "SOURCE", + "REPOSITORY", "COMMIT", "REVISION", "SEMANTIC_MAJOR", "ON_DELETE", + "RETAIN_OTHER", "KEYED", "PUBLIC_TRAVERSAL", "ID", "DOC", "MODE", + "EMITS", "RECEIVER", "REQUIRES", "ANY", "GET", "SET", "WATCH", "START", + "STOP", "READ", "WRITE", "RESOLVE", "CONNECT", "DISCONNECT", "CALL", + "WATCH_START", "WATCH_STOP", "SUBSCRIBE", "UNSUBSCRIBE", "OPTIMISTIC_REGISTER", + "CRDT", "OPTIONAL_ONE", "EXACTLY_ONE", "MANY_UNIQUE", "MANY", "ORDERED", + "UNIT", "WATCH_HANDLE", "MESSAGE", "ATOM_REF", "INTERFACE_REF", + "OPTIONAL", "LIST", "RECORD", "BOOL", "BYTES", "DOUBLE", "INT32", + "INT64", "STRING", "UINT32", "UINT64", "TRUE", "FALSE", "NULL", + "ARROW", "COLON", "SEMI", "COMMA", "DOT", "LBRACE", "RBRACE", "LBRACK", + "RBRACK", "LPAREN", "RPAREN", "LT", "GT", "AMP", "EQUAL", "INTEGER", + "JSON_NUMBER", "IDENTIFIER", "STRING_LITERAL", "ESC", "HEX", "LINE_COMMENT", + "BLOCK_COMMENT", "WS", ]; @@ -205,7 +215,7 @@ export class QuixosCapabilityLexer extends antlr.Lexer { public get modeNames(): string[] { return QuixosCapabilityLexer.modeNames; } public static readonly _serializedATN: number[] = [ - 4,0,110,1056,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7, + 4,0,117,1110,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7, 5,2,6,7,6,2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12, 2,13,7,13,2,14,7,14,2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19, 7,19,2,20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25, @@ -222,368 +232,390 @@ export class QuixosCapabilityLexer extends antlr.Lexer { 2,91,7,91,2,92,7,92,2,93,7,93,2,94,7,94,2,95,7,95,2,96,7,96,2,97, 7,97,2,98,7,98,2,99,7,99,2,100,7,100,2,101,7,101,2,102,7,102,2,103, 7,103,2,104,7,104,2,105,7,105,2,106,7,106,2,107,7,107,2,108,7,108, - 2,109,7,109,2,110,7,110,2,111,7,111,1,0,1,0,1,0,1,0,1,0,1,0,1,0, - 1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,2,1,2,1,2, - 1,2,1,2,1,2,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,4,1,4,1,4,1,4, - 1,4,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,6,1,6,1,6,1,6,1,6, - 1,6,1,6,1,6,1,6,1,6,1,6,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,8,1,8, - 1,8,1,8,1,8,1,8,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,10,1,10,1, - 10,1,10,1,10,1,10,1,10,1,10,1,10,1,10,1,11,1,11,1,11,1,11,1,11,1, - 11,1,11,1,11,1,11,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1, - 12,1,12,1,12,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1, - 13,1,14,1,14,1,14,1,14,1,14,1,14,1,15,1,15,1,15,1,15,1,15,1,15,1, - 15,1,15,1,16,1,16,1,16,1,17,1,17,1,17,1,17,1,17,1,18,1,18,1,18,1, - 19,1,19,1,19,1,19,1,19,1,19,1,19,1,19,1,20,1,20,1,20,1,20,1,20,1, - 20,1,20,1,21,1,21,1,21,1,21,1,21,1,21,1,22,1,22,1,22,1,22,1,22,1, - 23,1,23,1,23,1,23,1,23,1,23,1,23,1,23,1,23,1,23,1,23,1,24,1,24,1, - 24,1,24,1,24,1,25,1,25,1,25,1,25,1,25,1,25,1,26,1,26,1,26,1,26,1, - 27,1,27,1,27,1,27,1,27,1,27,1,27,1,27,1,27,1,27,1,27,1,27,1,28,1, - 28,1,28,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,30,1,30,1,30,1,31,1, - 31,1,31,1,31,1,31,1,31,1,31,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1, - 32,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,34,1,34,1,34,1,34,1,34,1, - 34,1,34,1,34,1,34,1,34,1,34,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1, - 36,1,36,1,36,1,36,1,36,1,36,1,36,1,36,1,36,1,37,1,37,1,37,1,37,1, - 37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,38,1,38,1, - 38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,39,1,39,1,39,1,39,1,39,1, + 2,109,7,109,2,110,7,110,2,111,7,111,2,112,7,112,2,113,7,113,2,114, + 7,114,2,115,7,115,2,116,7,116,2,117,7,117,2,118,7,118,1,0,1,0,1, + 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,2,1,2,1,2,1, + 2,1,2,1,2,1,2,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,4,1,4,1,4,1, + 4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,5,1,5,1,5,1,5,1,6,1,6,1,6,1,6,1, + 6,1,6,1,6,1,6,1,6,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,8,1,8,1,8,1,8,1, + 8,1,8,1,8,1,8,1,8,1,9,1,9,1,9,1,9,1,9,1,10,1,10,1,10,1,10,1,10,1, + 10,1,10,1,10,1,10,1,10,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1, + 11,1,11,1,11,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,13,1,13,1, + 13,1,13,1,13,1,13,1,14,1,14,1,14,1,14,1,14,1,14,1,14,1,14,1,14,1, + 15,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,16,1,16,1,16,1, + 16,1,16,1,16,1,16,1,16,1,16,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1, + 17,1,17,1,17,1,17,1,17,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1, + 18,1,18,1,18,1,19,1,19,1,19,1,19,1,19,1,19,1,20,1,20,1,20,1,20,1, + 20,1,20,1,20,1,20,1,21,1,21,1,21,1,22,1,22,1,22,1,22,1,22,1,23,1, + 23,1,23,1,24,1,24,1,24,1,24,1,24,1,24,1,24,1,24,1,25,1,25,1,25,1, + 25,1,25,1,25,1,25,1,26,1,26,1,26,1,26,1,26,1,26,1,27,1,27,1,27,1, + 27,1,27,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1, + 29,1,29,1,29,1,29,1,29,1,30,1,30,1,30,1,30,1,30,1,30,1,31,1,31,1, + 31,1,31,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1, + 32,1,33,1,33,1,33,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,35,1,35,1, + 35,1,36,1,36,1,36,1,36,1,36,1,36,1,36,1,37,1,37,1,37,1,37,1,37,1, + 37,1,37,1,37,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,39,1,39,1,39,1, 39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,40,1,40,1,40,1,40,1,40,1, - 40,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1, - 41,1,41,1,41,1,41,1,41,1,42,1,42,1,42,1,43,1,43,1,43,1,43,1,44,1, - 44,1,44,1,44,1,44,1,45,1,45,1,45,1,45,1,45,1,45,1,46,1,46,1,46,1, - 46,1,46,1,46,1,46,1,46,1,46,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1, - 47,1,47,1,48,1,48,1,48,1,48,1,49,1,49,1,49,1,49,1,50,1,50,1,50,1, - 50,1,51,1,51,1,51,1,51,1,51,1,51,1,52,1,52,1,52,1,52,1,52,1,52,1, - 53,1,53,1,53,1,53,1,53,1,54,1,54,1,54,1,54,1,54,1,55,1,55,1,55,1, - 55,1,55,1,55,1,56,1,56,1,56,1,56,1,56,1,56,1,56,1,56,1,57,1,57,1, - 57,1,57,1,57,1,57,1,57,1,57,1,58,1,58,1,58,1,58,1,58,1,58,1,58,1, - 58,1,58,1,58,1,58,1,59,1,59,1,59,1,59,1,59,1,60,1,60,1,60,1,60,1, - 60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,61,1,61,1,61,1,61,1,61,1, - 61,1,61,1,61,1,61,1,61,1,61,1,62,1,62,1,62,1,62,1,62,1,62,1,62,1, - 62,1,62,1,62,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1, - 63,1,63,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1, - 64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,65,1,65,1,65,1,65,1, - 65,1,66,1,66,1,66,1,66,1,66,1,66,1,66,1,66,1,66,1,66,1,66,1,66,1, - 66,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1, - 68,1,68,1,68,1,68,1,68,1,68,1,68,1,68,1,68,1,68,1,68,1,68,1,69,1, - 69,1,69,1,69,1,69,1,70,1,70,1,70,1,70,1,70,1,70,1,70,1,70,1,71,1, - 71,1,71,1,71,1,71,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1, - 72,1,72,1,72,1,72,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,74,1, - 74,1,74,1,74,1,74,1,74,1,74,1,74,1,74,1,75,1,75,1,75,1,75,1,75,1, - 75,1,75,1,75,1,75,1,75,1,75,1,75,1,75,1,75,1,76,1,76,1,76,1,76,1, - 76,1,76,1,76,1,76,1,76,1,77,1,77,1,77,1,77,1,77,1,78,1,78,1,78,1, - 78,1,78,1,78,1,78,1,79,1,79,1,79,1,79,1,79,1,80,1,80,1,80,1,80,1, - 80,1,80,1,81,1,81,1,81,1,81,1,81,1,81,1,81,1,82,1,82,1,82,1,82,1, - 82,1,82,1,83,1,83,1,83,1,83,1,83,1,83,1,84,1,84,1,84,1,84,1,84,1, - 84,1,84,1,85,1,85,1,85,1,85,1,85,1,85,1,85,1,86,1,86,1,86,1,86,1, - 86,1,86,1,86,1,87,1,87,1,87,1,87,1,87,1,88,1,88,1,88,1,88,1,88,1, - 88,1,89,1,89,1,89,1,89,1,89,1,90,1,90,1,90,1,91,1,91,1,92,1,92,1, - 93,1,93,1,94,1,94,1,95,1,95,1,96,1,96,1,97,1,97,1,98,1,98,1,99,1, - 99,1,100,1,100,1,101,1,101,1,102,1,102,1,103,3,103,957,8,103,1,103, - 4,103,960,8,103,11,103,12,103,961,1,104,3,104,965,8,104,1,104,1, - 104,1,104,5,104,970,8,104,10,104,12,104,973,9,104,3,104,975,8,104, - 1,104,1,104,4,104,979,8,104,11,104,12,104,980,3,104,983,8,104,1, - 104,1,104,3,104,987,8,104,1,104,4,104,990,8,104,11,104,12,104,991, - 3,104,994,8,104,1,105,1,105,5,105,998,8,105,10,105,12,105,1001,9, - 105,1,106,1,106,1,106,5,106,1006,8,106,10,106,12,106,1009,9,106, - 1,106,1,106,1,107,1,107,1,107,1,107,1,107,1,107,1,107,1,107,3,107, - 1021,8,107,1,108,1,108,1,109,1,109,1,109,1,109,5,109,1029,8,109, - 10,109,12,109,1032,9,109,1,109,1,109,1,110,1,110,1,110,1,110,5,110, - 1040,8,110,10,110,12,110,1043,9,110,1,110,1,110,1,110,1,110,1,110, - 1,111,4,111,1051,8,111,11,111,12,111,1052,1,111,1,111,1,1041,0,112, - 1,1,3,2,5,3,7,4,9,5,11,6,13,7,15,8,17,9,19,10,21,11,23,12,25,13, - 27,14,29,15,31,16,33,17,35,18,37,19,39,20,41,21,43,22,45,23,47,24, - 49,25,51,26,53,27,55,28,57,29,59,30,61,31,63,32,65,33,67,34,69,35, - 71,36,73,37,75,38,77,39,79,40,81,41,83,42,85,43,87,44,89,45,91,46, - 93,47,95,48,97,49,99,50,101,51,103,52,105,53,107,54,109,55,111,56, - 113,57,115,58,117,59,119,60,121,61,123,62,125,63,127,64,129,65,131, - 66,133,67,135,68,137,69,139,70,141,71,143,72,145,73,147,74,149,75, - 151,76,153,77,155,78,157,79,159,80,161,81,163,82,165,83,167,84,169, - 85,171,86,173,87,175,88,177,89,179,90,181,91,183,92,185,93,187,94, - 189,95,191,96,193,97,195,98,197,99,199,100,201,101,203,102,205,103, - 207,104,209,105,211,106,213,107,215,0,217,0,219,108,221,109,223, - 110,1,0,11,1,0,48,57,1,0,49,57,2,0,69,69,101,101,2,0,43,43,45,45, - 3,0,65,90,95,95,97,122,4,0,48,57,65,90,95,95,97,122,4,0,10,10,13, - 13,34,34,92,92,8,0,34,34,47,47,92,92,98,98,102,102,110,110,114,114, - 116,116,3,0,48,57,65,70,97,102,2,0,10,10,13,13,3,0,9,10,13,13,32, - 32,1070,0,1,1,0,0,0,0,3,1,0,0,0,0,5,1,0,0,0,0,7,1,0,0,0,0,9,1,0, - 0,0,0,11,1,0,0,0,0,13,1,0,0,0,0,15,1,0,0,0,0,17,1,0,0,0,0,19,1,0, - 0,0,0,21,1,0,0,0,0,23,1,0,0,0,0,25,1,0,0,0,0,27,1,0,0,0,0,29,1,0, - 0,0,0,31,1,0,0,0,0,33,1,0,0,0,0,35,1,0,0,0,0,37,1,0,0,0,0,39,1,0, - 0,0,0,41,1,0,0,0,0,43,1,0,0,0,0,45,1,0,0,0,0,47,1,0,0,0,0,49,1,0, - 0,0,0,51,1,0,0,0,0,53,1,0,0,0,0,55,1,0,0,0,0,57,1,0,0,0,0,59,1,0, - 0,0,0,61,1,0,0,0,0,63,1,0,0,0,0,65,1,0,0,0,0,67,1,0,0,0,0,69,1,0, - 0,0,0,71,1,0,0,0,0,73,1,0,0,0,0,75,1,0,0,0,0,77,1,0,0,0,0,79,1,0, - 0,0,0,81,1,0,0,0,0,83,1,0,0,0,0,85,1,0,0,0,0,87,1,0,0,0,0,89,1,0, - 0,0,0,91,1,0,0,0,0,93,1,0,0,0,0,95,1,0,0,0,0,97,1,0,0,0,0,99,1,0, - 0,0,0,101,1,0,0,0,0,103,1,0,0,0,0,105,1,0,0,0,0,107,1,0,0,0,0,109, - 1,0,0,0,0,111,1,0,0,0,0,113,1,0,0,0,0,115,1,0,0,0,0,117,1,0,0,0, - 0,119,1,0,0,0,0,121,1,0,0,0,0,123,1,0,0,0,0,125,1,0,0,0,0,127,1, - 0,0,0,0,129,1,0,0,0,0,131,1,0,0,0,0,133,1,0,0,0,0,135,1,0,0,0,0, - 137,1,0,0,0,0,139,1,0,0,0,0,141,1,0,0,0,0,143,1,0,0,0,0,145,1,0, - 0,0,0,147,1,0,0,0,0,149,1,0,0,0,0,151,1,0,0,0,0,153,1,0,0,0,0,155, - 1,0,0,0,0,157,1,0,0,0,0,159,1,0,0,0,0,161,1,0,0,0,0,163,1,0,0,0, - 0,165,1,0,0,0,0,167,1,0,0,0,0,169,1,0,0,0,0,171,1,0,0,0,0,173,1, - 0,0,0,0,175,1,0,0,0,0,177,1,0,0,0,0,179,1,0,0,0,0,181,1,0,0,0,0, - 183,1,0,0,0,0,185,1,0,0,0,0,187,1,0,0,0,0,189,1,0,0,0,0,191,1,0, - 0,0,0,193,1,0,0,0,0,195,1,0,0,0,0,197,1,0,0,0,0,199,1,0,0,0,0,201, - 1,0,0,0,0,203,1,0,0,0,0,205,1,0,0,0,0,207,1,0,0,0,0,209,1,0,0,0, - 0,211,1,0,0,0,0,213,1,0,0,0,0,219,1,0,0,0,0,221,1,0,0,0,0,223,1, - 0,0,0,1,225,1,0,0,0,3,235,1,0,0,0,5,244,1,0,0,0,7,251,1,0,0,0,9, - 260,1,0,0,0,11,265,1,0,0,0,13,275,1,0,0,0,15,286,1,0,0,0,17,294, - 1,0,0,0,19,300,1,0,0,0,21,309,1,0,0,0,23,319,1,0,0,0,25,328,1,0, - 0,0,27,340,1,0,0,0,29,351,1,0,0,0,31,357,1,0,0,0,33,365,1,0,0,0, - 35,368,1,0,0,0,37,373,1,0,0,0,39,376,1,0,0,0,41,384,1,0,0,0,43,391, - 1,0,0,0,45,397,1,0,0,0,47,402,1,0,0,0,49,413,1,0,0,0,51,418,1,0, - 0,0,53,424,1,0,0,0,55,428,1,0,0,0,57,440,1,0,0,0,59,443,1,0,0,0, - 61,450,1,0,0,0,63,453,1,0,0,0,65,460,1,0,0,0,67,468,1,0,0,0,69,475, - 1,0,0,0,71,486,1,0,0,0,73,493,1,0,0,0,75,502,1,0,0,0,77,517,1,0, - 0,0,79,527,1,0,0,0,81,540,1,0,0,0,83,546,1,0,0,0,85,563,1,0,0,0, - 87,566,1,0,0,0,89,570,1,0,0,0,91,575,1,0,0,0,93,581,1,0,0,0,95,590, - 1,0,0,0,97,599,1,0,0,0,99,603,1,0,0,0,101,607,1,0,0,0,103,611,1, - 0,0,0,105,617,1,0,0,0,107,623,1,0,0,0,109,628,1,0,0,0,111,633,1, - 0,0,0,113,639,1,0,0,0,115,647,1,0,0,0,117,655,1,0,0,0,119,666,1, - 0,0,0,121,671,1,0,0,0,123,683,1,0,0,0,125,694,1,0,0,0,127,704,1, - 0,0,0,129,716,1,0,0,0,131,736,1,0,0,0,133,741,1,0,0,0,135,754,1, - 0,0,0,137,766,1,0,0,0,139,778,1,0,0,0,141,783,1,0,0,0,143,791,1, - 0,0,0,145,796,1,0,0,0,147,809,1,0,0,0,149,817,1,0,0,0,151,826,1, - 0,0,0,153,840,1,0,0,0,155,849,1,0,0,0,157,854,1,0,0,0,159,861,1, - 0,0,0,161,866,1,0,0,0,163,872,1,0,0,0,165,879,1,0,0,0,167,885,1, - 0,0,0,169,891,1,0,0,0,171,898,1,0,0,0,173,905,1,0,0,0,175,912,1, - 0,0,0,177,917,1,0,0,0,179,923,1,0,0,0,181,928,1,0,0,0,183,931,1, - 0,0,0,185,933,1,0,0,0,187,935,1,0,0,0,189,937,1,0,0,0,191,939,1, - 0,0,0,193,941,1,0,0,0,195,943,1,0,0,0,197,945,1,0,0,0,199,947,1, - 0,0,0,201,949,1,0,0,0,203,951,1,0,0,0,205,953,1,0,0,0,207,956,1, - 0,0,0,209,964,1,0,0,0,211,995,1,0,0,0,213,1002,1,0,0,0,215,1012, - 1,0,0,0,217,1022,1,0,0,0,219,1024,1,0,0,0,221,1035,1,0,0,0,223,1050, - 1,0,0,0,225,226,5,119,0,0,226,227,5,111,0,0,227,228,5,114,0,0,228, - 229,5,107,0,0,229,230,5,115,0,0,230,231,5,112,0,0,231,232,5,97,0, - 0,232,233,5,99,0,0,233,234,5,101,0,0,234,2,1,0,0,0,235,236,5,102, - 0,0,236,237,5,114,0,0,237,238,5,97,0,0,238,239,5,103,0,0,239,240, - 5,109,0,0,240,241,5,101,0,0,241,242,5,110,0,0,242,243,5,116,0,0, - 243,4,1,0,0,0,244,245,5,105,0,0,245,246,5,109,0,0,246,247,5,112, - 0,0,247,248,5,111,0,0,248,249,5,114,0,0,249,250,5,116,0,0,250,6, - 1,0,0,0,251,252,5,101,0,0,252,253,5,120,0,0,253,254,5,116,0,0,254, - 255,5,101,0,0,255,256,5,114,0,0,256,257,5,110,0,0,257,258,5,97,0, - 0,258,259,5,108,0,0,259,8,1,0,0,0,260,261,5,97,0,0,261,262,5,116, - 0,0,262,263,5,111,0,0,263,264,5,109,0,0,264,10,1,0,0,0,265,266,5, - 105,0,0,266,267,5,110,0,0,267,268,5,116,0,0,268,269,5,101,0,0,269, - 270,5,114,0,0,270,271,5,102,0,0,271,272,5,97,0,0,272,273,5,99,0, - 0,273,274,5,101,0,0,274,12,1,0,0,0,275,276,5,105,0,0,276,277,5,110, - 0,0,277,278,5,116,0,0,278,279,5,101,0,0,279,280,5,114,0,0,280,281, - 5,102,0,0,281,282,5,97,0,0,282,283,5,99,0,0,283,284,5,101,0,0,284, - 285,5,115,0,0,285,14,1,0,0,0,286,287,5,112,0,0,287,288,5,97,0,0, - 288,289,5,99,0,0,289,290,5,107,0,0,290,291,5,97,0,0,291,292,5,103, - 0,0,292,293,5,101,0,0,293,16,1,0,0,0,294,295,5,118,0,0,295,296,5, - 97,0,0,296,297,5,108,0,0,297,298,5,117,0,0,298,299,5,101,0,0,299, - 18,1,0,0,0,300,301,5,114,0,0,301,302,5,101,0,0,302,303,5,108,0,0, - 303,304,5,97,0,0,304,305,5,116,0,0,305,306,5,105,0,0,306,307,5,111, - 0,0,307,308,5,110,0,0,308,20,1,0,0,0,309,310,5,111,0,0,310,311,5, - 112,0,0,311,312,5,101,0,0,312,313,5,114,0,0,313,314,5,97,0,0,314, - 315,5,116,0,0,315,316,5,105,0,0,316,317,5,111,0,0,317,318,5,110, - 0,0,318,22,1,0,0,0,319,320,5,102,0,0,320,321,5,117,0,0,321,322,5, - 110,0,0,322,323,5,99,0,0,323,324,5,116,0,0,324,325,5,105,0,0,325, - 326,5,111,0,0,326,327,5,110,0,0,327,24,1,0,0,0,328,329,5,99,0,0, - 329,330,5,111,0,0,330,331,5,110,0,0,331,332,5,115,0,0,332,333,5, - 116,0,0,333,334,5,114,0,0,334,335,5,117,0,0,335,336,5,99,0,0,336, - 337,5,116,0,0,337,338,5,111,0,0,338,339,5,114,0,0,339,26,1,0,0,0, - 340,341,5,99,0,0,341,342,5,111,0,0,342,343,5,110,0,0,343,344,5,115, - 0,0,344,345,5,116,0,0,345,346,5,114,0,0,346,347,5,117,0,0,347,348, - 5,99,0,0,348,349,5,116,0,0,349,350,5,115,0,0,350,28,1,0,0,0,351, - 352,5,105,0,0,352,353,5,110,0,0,353,354,5,112,0,0,354,355,5,117, - 0,0,355,356,5,116,0,0,356,30,1,0,0,0,357,358,5,99,0,0,358,359,5, - 111,0,0,359,360,5,110,0,0,360,361,5,102,0,0,361,362,5,111,0,0,362, - 363,5,114,0,0,363,364,5,109,0,0,364,32,1,0,0,0,365,366,5,97,0,0, - 366,367,5,115,0,0,367,34,1,0,0,0,368,369,5,98,0,0,369,370,5,105, - 0,0,370,371,5,110,0,0,371,372,5,100,0,0,372,36,1,0,0,0,373,374,5, - 116,0,0,374,375,5,111,0,0,375,38,1,0,0,0,376,377,5,112,0,0,377,378, - 5,114,0,0,378,379,5,105,0,0,379,380,5,118,0,0,380,381,5,97,0,0,381, - 382,5,116,0,0,382,383,5,101,0,0,383,40,1,0,0,0,384,385,5,115,0,0, - 385,386,5,104,0,0,386,387,5,97,0,0,387,388,5,114,0,0,388,389,5,101, - 0,0,389,390,5,100,0,0,390,42,1,0,0,0,391,392,5,115,0,0,392,393,5, - 116,0,0,393,394,5,97,0,0,394,395,5,116,0,0,395,396,5,101,0,0,396, - 44,1,0,0,0,397,398,5,101,0,0,398,399,5,100,0,0,399,400,5,103,0,0, - 400,401,5,101,0,0,401,46,1,0,0,0,402,403,5,112,0,0,403,404,5,114, - 0,0,404,405,5,111,0,0,405,406,5,106,0,0,406,407,5,101,0,0,407,408, - 5,99,0,0,408,409,5,116,0,0,409,410,5,105,0,0,410,411,5,111,0,0,411, - 412,5,110,0,0,412,48,1,0,0,0,413,414,5,119,0,0,414,415,5,105,0,0, - 415,416,5,116,0,0,416,417,5,104,0,0,417,50,1,0,0,0,418,419,5,117, - 0,0,419,420,5,115,0,0,420,421,5,105,0,0,421,422,5,110,0,0,422,423, - 5,103,0,0,423,52,1,0,0,0,424,425,5,118,0,0,425,426,5,105,0,0,426, - 427,5,97,0,0,427,54,1,0,0,0,428,429,5,109,0,0,429,430,5,97,0,0,430, - 431,5,116,0,0,431,432,5,101,0,0,432,433,5,114,0,0,433,434,5,105, - 0,0,434,435,5,97,0,0,435,436,5,108,0,0,436,437,5,105,0,0,437,438, - 5,122,0,0,438,439,5,101,0,0,439,56,1,0,0,0,440,441,5,105,0,0,441, - 442,5,102,0,0,442,58,1,0,0,0,443,444,5,97,0,0,444,445,5,98,0,0,445, - 446,5,115,0,0,446,447,5,101,0,0,447,448,5,110,0,0,448,449,5,116, - 0,0,449,60,1,0,0,0,450,451,5,111,0,0,451,452,5,110,0,0,452,62,1, - 0,0,0,453,454,5,112,0,0,454,455,5,111,0,0,455,456,5,108,0,0,456, - 457,5,105,0,0,457,458,5,99,0,0,458,459,5,121,0,0,459,64,1,0,0,0, - 460,461,5,100,0,0,461,462,5,101,0,0,462,463,5,102,0,0,463,464,5, - 97,0,0,464,465,5,117,0,0,465,466,5,108,0,0,466,467,5,116,0,0,467, - 66,1,0,0,0,468,469,5,115,0,0,469,470,5,111,0,0,470,471,5,117,0,0, - 471,472,5,114,0,0,472,473,5,99,0,0,473,474,5,101,0,0,474,68,1,0, - 0,0,475,476,5,114,0,0,476,477,5,101,0,0,477,478,5,112,0,0,478,479, - 5,111,0,0,479,480,5,115,0,0,480,481,5,105,0,0,481,482,5,116,0,0, - 482,483,5,111,0,0,483,484,5,114,0,0,484,485,5,121,0,0,485,70,1,0, - 0,0,486,487,5,99,0,0,487,488,5,111,0,0,488,489,5,109,0,0,489,490, - 5,109,0,0,490,491,5,105,0,0,491,492,5,116,0,0,492,72,1,0,0,0,493, - 494,5,114,0,0,494,495,5,101,0,0,495,496,5,118,0,0,496,497,5,105, - 0,0,497,498,5,115,0,0,498,499,5,105,0,0,499,500,5,111,0,0,500,501, - 5,110,0,0,501,74,1,0,0,0,502,503,5,115,0,0,503,504,5,101,0,0,504, - 505,5,109,0,0,505,506,5,97,0,0,506,507,5,110,0,0,507,508,5,116,0, - 0,508,509,5,105,0,0,509,510,5,99,0,0,510,511,5,45,0,0,511,512,5, - 109,0,0,512,513,5,97,0,0,513,514,5,106,0,0,514,515,5,111,0,0,515, - 516,5,114,0,0,516,76,1,0,0,0,517,518,5,111,0,0,518,519,5,110,0,0, - 519,520,5,45,0,0,520,521,5,100,0,0,521,522,5,101,0,0,522,523,5,108, - 0,0,523,524,5,101,0,0,524,525,5,116,0,0,525,526,5,101,0,0,526,78, - 1,0,0,0,527,528,5,114,0,0,528,529,5,101,0,0,529,530,5,116,0,0,530, - 531,5,97,0,0,531,532,5,105,0,0,532,533,5,110,0,0,533,534,5,45,0, - 0,534,535,5,111,0,0,535,536,5,116,0,0,536,537,5,104,0,0,537,538, - 5,101,0,0,538,539,5,114,0,0,539,80,1,0,0,0,540,541,5,107,0,0,541, - 542,5,101,0,0,542,543,5,121,0,0,543,544,5,101,0,0,544,545,5,100, - 0,0,545,82,1,0,0,0,546,547,5,112,0,0,547,548,5,117,0,0,548,549,5, - 98,0,0,549,550,5,108,0,0,550,551,5,105,0,0,551,552,5,99,0,0,552, - 553,5,45,0,0,553,554,5,116,0,0,554,555,5,114,0,0,555,556,5,97,0, - 0,556,557,5,118,0,0,557,558,5,101,0,0,558,559,5,114,0,0,559,560, - 5,115,0,0,560,561,5,97,0,0,561,562,5,108,0,0,562,84,1,0,0,0,563, - 564,5,105,0,0,564,565,5,100,0,0,565,86,1,0,0,0,566,567,5,100,0,0, - 567,568,5,111,0,0,568,569,5,99,0,0,569,88,1,0,0,0,570,571,5,109, - 0,0,571,572,5,111,0,0,572,573,5,100,0,0,573,574,5,101,0,0,574,90, - 1,0,0,0,575,576,5,101,0,0,576,577,5,109,0,0,577,578,5,105,0,0,578, - 579,5,116,0,0,579,580,5,115,0,0,580,92,1,0,0,0,581,582,5,114,0,0, - 582,583,5,101,0,0,583,584,5,99,0,0,584,585,5,101,0,0,585,586,5,105, - 0,0,586,587,5,118,0,0,587,588,5,101,0,0,588,589,5,114,0,0,589,94, - 1,0,0,0,590,591,5,114,0,0,591,592,5,101,0,0,592,593,5,113,0,0,593, - 594,5,117,0,0,594,595,5,105,0,0,595,596,5,114,0,0,596,597,5,101, - 0,0,597,598,5,115,0,0,598,96,1,0,0,0,599,600,5,97,0,0,600,601,5, - 110,0,0,601,602,5,121,0,0,602,98,1,0,0,0,603,604,5,103,0,0,604,605, - 5,101,0,0,605,606,5,116,0,0,606,100,1,0,0,0,607,608,5,115,0,0,608, - 609,5,101,0,0,609,610,5,116,0,0,610,102,1,0,0,0,611,612,5,119,0, - 0,612,613,5,97,0,0,613,614,5,116,0,0,614,615,5,99,0,0,615,616,5, - 104,0,0,616,104,1,0,0,0,617,618,5,115,0,0,618,619,5,116,0,0,619, - 620,5,97,0,0,620,621,5,114,0,0,621,622,5,116,0,0,622,106,1,0,0,0, - 623,624,5,115,0,0,624,625,5,116,0,0,625,626,5,111,0,0,626,627,5, - 112,0,0,627,108,1,0,0,0,628,629,5,114,0,0,629,630,5,101,0,0,630, - 631,5,97,0,0,631,632,5,100,0,0,632,110,1,0,0,0,633,634,5,119,0,0, - 634,635,5,114,0,0,635,636,5,105,0,0,636,637,5,116,0,0,637,638,5, - 101,0,0,638,112,1,0,0,0,639,640,5,114,0,0,640,641,5,101,0,0,641, - 642,5,115,0,0,642,643,5,111,0,0,643,644,5,108,0,0,644,645,5,118, - 0,0,645,646,5,101,0,0,646,114,1,0,0,0,647,648,5,99,0,0,648,649,5, - 111,0,0,649,650,5,110,0,0,650,651,5,110,0,0,651,652,5,101,0,0,652, - 653,5,99,0,0,653,654,5,116,0,0,654,116,1,0,0,0,655,656,5,100,0,0, - 656,657,5,105,0,0,657,658,5,115,0,0,658,659,5,99,0,0,659,660,5,111, - 0,0,660,661,5,110,0,0,661,662,5,110,0,0,662,663,5,101,0,0,663,664, - 5,99,0,0,664,665,5,116,0,0,665,118,1,0,0,0,666,667,5,99,0,0,667, - 668,5,97,0,0,668,669,5,108,0,0,669,670,5,108,0,0,670,120,1,0,0,0, - 671,672,5,119,0,0,672,673,5,97,0,0,673,674,5,116,0,0,674,675,5,99, - 0,0,675,676,5,104,0,0,676,677,5,45,0,0,677,678,5,115,0,0,678,679, - 5,116,0,0,679,680,5,97,0,0,680,681,5,114,0,0,681,682,5,116,0,0,682, - 122,1,0,0,0,683,684,5,119,0,0,684,685,5,97,0,0,685,686,5,116,0,0, - 686,687,5,99,0,0,687,688,5,104,0,0,688,689,5,45,0,0,689,690,5,115, - 0,0,690,691,5,116,0,0,691,692,5,111,0,0,692,693,5,112,0,0,693,124, - 1,0,0,0,694,695,5,115,0,0,695,696,5,117,0,0,696,697,5,98,0,0,697, - 698,5,115,0,0,698,699,5,99,0,0,699,700,5,114,0,0,700,701,5,105,0, - 0,701,702,5,98,0,0,702,703,5,101,0,0,703,126,1,0,0,0,704,705,5,117, - 0,0,705,706,5,110,0,0,706,707,5,115,0,0,707,708,5,117,0,0,708,709, - 5,98,0,0,709,710,5,115,0,0,710,711,5,99,0,0,711,712,5,114,0,0,712, - 713,5,105,0,0,713,714,5,98,0,0,714,715,5,101,0,0,715,128,1,0,0,0, - 716,717,5,111,0,0,717,718,5,112,0,0,718,719,5,116,0,0,719,720,5, - 105,0,0,720,721,5,109,0,0,721,722,5,105,0,0,722,723,5,115,0,0,723, - 724,5,116,0,0,724,725,5,105,0,0,725,726,5,99,0,0,726,727,5,45,0, - 0,727,728,5,114,0,0,728,729,5,101,0,0,729,730,5,103,0,0,730,731, - 5,105,0,0,731,732,5,115,0,0,732,733,5,116,0,0,733,734,5,101,0,0, - 734,735,5,114,0,0,735,130,1,0,0,0,736,737,5,99,0,0,737,738,5,114, - 0,0,738,739,5,100,0,0,739,740,5,116,0,0,740,132,1,0,0,0,741,742, - 5,111,0,0,742,743,5,112,0,0,743,744,5,116,0,0,744,745,5,105,0,0, - 745,746,5,111,0,0,746,747,5,110,0,0,747,748,5,97,0,0,748,749,5,108, - 0,0,749,750,5,45,0,0,750,751,5,111,0,0,751,752,5,110,0,0,752,753, - 5,101,0,0,753,134,1,0,0,0,754,755,5,101,0,0,755,756,5,120,0,0,756, - 757,5,97,0,0,757,758,5,99,0,0,758,759,5,116,0,0,759,760,5,108,0, - 0,760,761,5,121,0,0,761,762,5,45,0,0,762,763,5,111,0,0,763,764,5, - 110,0,0,764,765,5,101,0,0,765,136,1,0,0,0,766,767,5,109,0,0,767, - 768,5,97,0,0,768,769,5,110,0,0,769,770,5,121,0,0,770,771,5,45,0, - 0,771,772,5,117,0,0,772,773,5,110,0,0,773,774,5,105,0,0,774,775, - 5,113,0,0,775,776,5,117,0,0,776,777,5,101,0,0,777,138,1,0,0,0,778, - 779,5,109,0,0,779,780,5,97,0,0,780,781,5,110,0,0,781,782,5,121,0, - 0,782,140,1,0,0,0,783,784,5,111,0,0,784,785,5,114,0,0,785,786,5, - 100,0,0,786,787,5,101,0,0,787,788,5,114,0,0,788,789,5,101,0,0,789, - 790,5,100,0,0,790,142,1,0,0,0,791,792,5,117,0,0,792,793,5,110,0, - 0,793,794,5,105,0,0,794,795,5,116,0,0,795,144,1,0,0,0,796,797,5, - 119,0,0,797,798,5,97,0,0,798,799,5,116,0,0,799,800,5,99,0,0,800, - 801,5,104,0,0,801,802,5,45,0,0,802,803,5,104,0,0,803,804,5,97,0, - 0,804,805,5,110,0,0,805,806,5,100,0,0,806,807,5,108,0,0,807,808, - 5,101,0,0,808,146,1,0,0,0,809,810,5,109,0,0,810,811,5,101,0,0,811, - 812,5,115,0,0,812,813,5,115,0,0,813,814,5,97,0,0,814,815,5,103,0, - 0,815,816,5,101,0,0,816,148,1,0,0,0,817,818,5,97,0,0,818,819,5,116, - 0,0,819,820,5,111,0,0,820,821,5,109,0,0,821,822,5,45,0,0,822,823, - 5,114,0,0,823,824,5,101,0,0,824,825,5,102,0,0,825,150,1,0,0,0,826, - 827,5,105,0,0,827,828,5,110,0,0,828,829,5,116,0,0,829,830,5,101, - 0,0,830,831,5,114,0,0,831,832,5,102,0,0,832,833,5,97,0,0,833,834, - 5,99,0,0,834,835,5,101,0,0,835,836,5,45,0,0,836,837,5,114,0,0,837, - 838,5,101,0,0,838,839,5,102,0,0,839,152,1,0,0,0,840,841,5,111,0, - 0,841,842,5,112,0,0,842,843,5,116,0,0,843,844,5,105,0,0,844,845, - 5,111,0,0,845,846,5,110,0,0,846,847,5,97,0,0,847,848,5,108,0,0,848, - 154,1,0,0,0,849,850,5,108,0,0,850,851,5,105,0,0,851,852,5,115,0, - 0,852,853,5,116,0,0,853,156,1,0,0,0,854,855,5,114,0,0,855,856,5, - 101,0,0,856,857,5,99,0,0,857,858,5,111,0,0,858,859,5,114,0,0,859, - 860,5,100,0,0,860,158,1,0,0,0,861,862,5,98,0,0,862,863,5,111,0,0, - 863,864,5,111,0,0,864,865,5,108,0,0,865,160,1,0,0,0,866,867,5,98, - 0,0,867,868,5,121,0,0,868,869,5,116,0,0,869,870,5,101,0,0,870,871, - 5,115,0,0,871,162,1,0,0,0,872,873,5,100,0,0,873,874,5,111,0,0,874, - 875,5,117,0,0,875,876,5,98,0,0,876,877,5,108,0,0,877,878,5,101,0, - 0,878,164,1,0,0,0,879,880,5,105,0,0,880,881,5,110,0,0,881,882,5, - 116,0,0,882,883,5,51,0,0,883,884,5,50,0,0,884,166,1,0,0,0,885,886, - 5,105,0,0,886,887,5,110,0,0,887,888,5,116,0,0,888,889,5,54,0,0,889, - 890,5,52,0,0,890,168,1,0,0,0,891,892,5,115,0,0,892,893,5,116,0,0, - 893,894,5,114,0,0,894,895,5,105,0,0,895,896,5,110,0,0,896,897,5, - 103,0,0,897,170,1,0,0,0,898,899,5,117,0,0,899,900,5,105,0,0,900, - 901,5,110,0,0,901,902,5,116,0,0,902,903,5,51,0,0,903,904,5,50,0, - 0,904,172,1,0,0,0,905,906,5,117,0,0,906,907,5,105,0,0,907,908,5, - 110,0,0,908,909,5,116,0,0,909,910,5,54,0,0,910,911,5,52,0,0,911, - 174,1,0,0,0,912,913,5,116,0,0,913,914,5,114,0,0,914,915,5,117,0, - 0,915,916,5,101,0,0,916,176,1,0,0,0,917,918,5,102,0,0,918,919,5, - 97,0,0,919,920,5,108,0,0,920,921,5,115,0,0,921,922,5,101,0,0,922, - 178,1,0,0,0,923,924,5,110,0,0,924,925,5,117,0,0,925,926,5,108,0, - 0,926,927,5,108,0,0,927,180,1,0,0,0,928,929,5,45,0,0,929,930,5,62, - 0,0,930,182,1,0,0,0,931,932,5,58,0,0,932,184,1,0,0,0,933,934,5,59, - 0,0,934,186,1,0,0,0,935,936,5,44,0,0,936,188,1,0,0,0,937,938,5,46, - 0,0,938,190,1,0,0,0,939,940,5,123,0,0,940,192,1,0,0,0,941,942,5, - 125,0,0,942,194,1,0,0,0,943,944,5,91,0,0,944,196,1,0,0,0,945,946, - 5,93,0,0,946,198,1,0,0,0,947,948,5,40,0,0,948,200,1,0,0,0,949,950, - 5,41,0,0,950,202,1,0,0,0,951,952,5,60,0,0,952,204,1,0,0,0,953,954, - 5,62,0,0,954,206,1,0,0,0,955,957,5,45,0,0,956,955,1,0,0,0,956,957, - 1,0,0,0,957,959,1,0,0,0,958,960,7,0,0,0,959,958,1,0,0,0,960,961, - 1,0,0,0,961,959,1,0,0,0,961,962,1,0,0,0,962,208,1,0,0,0,963,965, - 5,45,0,0,964,963,1,0,0,0,964,965,1,0,0,0,965,974,1,0,0,0,966,975, - 5,48,0,0,967,971,7,1,0,0,968,970,7,0,0,0,969,968,1,0,0,0,970,973, - 1,0,0,0,971,969,1,0,0,0,971,972,1,0,0,0,972,975,1,0,0,0,973,971, - 1,0,0,0,974,966,1,0,0,0,974,967,1,0,0,0,975,982,1,0,0,0,976,978, - 5,46,0,0,977,979,7,0,0,0,978,977,1,0,0,0,979,980,1,0,0,0,980,978, - 1,0,0,0,980,981,1,0,0,0,981,983,1,0,0,0,982,976,1,0,0,0,982,983, - 1,0,0,0,983,993,1,0,0,0,984,986,7,2,0,0,985,987,7,3,0,0,986,985, - 1,0,0,0,986,987,1,0,0,0,987,989,1,0,0,0,988,990,7,0,0,0,989,988, - 1,0,0,0,990,991,1,0,0,0,991,989,1,0,0,0,991,992,1,0,0,0,992,994, - 1,0,0,0,993,984,1,0,0,0,993,994,1,0,0,0,994,210,1,0,0,0,995,999, - 7,4,0,0,996,998,7,5,0,0,997,996,1,0,0,0,998,1001,1,0,0,0,999,997, - 1,0,0,0,999,1000,1,0,0,0,1000,212,1,0,0,0,1001,999,1,0,0,0,1002, - 1007,5,34,0,0,1003,1006,3,215,107,0,1004,1006,8,6,0,0,1005,1003, - 1,0,0,0,1005,1004,1,0,0,0,1006,1009,1,0,0,0,1007,1005,1,0,0,0,1007, - 1008,1,0,0,0,1008,1010,1,0,0,0,1009,1007,1,0,0,0,1010,1011,5,34, - 0,0,1011,214,1,0,0,0,1012,1020,5,92,0,0,1013,1021,7,7,0,0,1014,1015, - 5,117,0,0,1015,1016,3,217,108,0,1016,1017,3,217,108,0,1017,1018, - 3,217,108,0,1018,1019,3,217,108,0,1019,1021,1,0,0,0,1020,1013,1, - 0,0,0,1020,1014,1,0,0,0,1021,216,1,0,0,0,1022,1023,7,8,0,0,1023, - 218,1,0,0,0,1024,1025,5,47,0,0,1025,1026,5,47,0,0,1026,1030,1,0, - 0,0,1027,1029,8,9,0,0,1028,1027,1,0,0,0,1029,1032,1,0,0,0,1030,1028, - 1,0,0,0,1030,1031,1,0,0,0,1031,1033,1,0,0,0,1032,1030,1,0,0,0,1033, - 1034,6,109,0,0,1034,220,1,0,0,0,1035,1036,5,47,0,0,1036,1037,5,42, - 0,0,1037,1041,1,0,0,0,1038,1040,9,0,0,0,1039,1038,1,0,0,0,1040,1043, - 1,0,0,0,1041,1042,1,0,0,0,1041,1039,1,0,0,0,1042,1044,1,0,0,0,1043, - 1041,1,0,0,0,1044,1045,5,42,0,0,1045,1046,5,47,0,0,1046,1047,1,0, - 0,0,1047,1048,6,110,0,0,1048,222,1,0,0,0,1049,1051,7,10,0,0,1050, - 1049,1,0,0,0,1051,1052,1,0,0,0,1052,1050,1,0,0,0,1052,1053,1,0,0, - 0,1053,1054,1,0,0,0,1054,1055,6,111,0,0,1055,224,1,0,0,0,18,0,956, - 961,964,971,974,980,982,986,991,993,999,1005,1007,1020,1030,1041, - 1052,1,0,1,0 + 40,1,40,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,42,1,42,1, + 42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1, + 43,1,43,1,43,1,43,1,43,1,43,1,43,1,43,1,43,1,43,1,44,1,44,1,44,1, + 44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,45,1,45,1,45,1, + 45,1,45,1,45,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1, + 46,1,46,1,46,1,46,1,46,1,46,1,46,1,47,1,47,1,47,1,48,1,48,1,48,1, + 48,1,49,1,49,1,49,1,49,1,49,1,50,1,50,1,50,1,50,1,50,1,50,1,51,1, + 51,1,51,1,51,1,51,1,51,1,51,1,51,1,51,1,52,1,52,1,52,1,52,1,52,1, + 52,1,52,1,52,1,52,1,53,1,53,1,53,1,53,1,54,1,54,1,54,1,54,1,55,1, + 55,1,55,1,55,1,56,1,56,1,56,1,56,1,56,1,56,1,57,1,57,1,57,1,57,1, + 57,1,57,1,58,1,58,1,58,1,58,1,58,1,59,1,59,1,59,1,59,1,59,1,60,1, + 60,1,60,1,60,1,60,1,60,1,61,1,61,1,61,1,61,1,61,1,61,1,61,1,61,1, + 62,1,62,1,62,1,62,1,62,1,62,1,62,1,62,1,63,1,63,1,63,1,63,1,63,1, + 63,1,63,1,63,1,63,1,63,1,63,1,64,1,64,1,64,1,64,1,64,1,65,1,65,1, + 65,1,65,1,65,1,65,1,65,1,65,1,65,1,65,1,65,1,65,1,66,1,66,1,66,1, + 66,1,66,1,66,1,66,1,66,1,66,1,66,1,66,1,67,1,67,1,67,1,67,1,67,1, + 67,1,67,1,67,1,67,1,67,1,68,1,68,1,68,1,68,1,68,1,68,1,68,1,68,1, + 68,1,68,1,68,1,68,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1, + 69,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,70,1,70,1, + 70,1,70,1,70,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1, + 71,1,71,1,71,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1, + 72,1,72,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1, + 73,1,74,1,74,1,74,1,74,1,74,1,75,1,75,1,75,1,75,1,75,1,75,1,75,1, + 75,1,76,1,76,1,76,1,76,1,76,1,77,1,77,1,77,1,77,1,77,1,77,1,77,1, + 77,1,77,1,77,1,77,1,77,1,77,1,78,1,78,1,78,1,78,1,78,1,78,1,78,1, + 78,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,80,1,80,1,80,1, + 80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,81,1,81,1, + 81,1,81,1,81,1,81,1,81,1,81,1,81,1,82,1,82,1,82,1,82,1,82,1,83,1, + 83,1,83,1,83,1,83,1,83,1,83,1,84,1,84,1,84,1,84,1,84,1,85,1,85,1, + 85,1,85,1,85,1,85,1,86,1,86,1,86,1,86,1,86,1,86,1,86,1,87,1,87,1, + 87,1,87,1,87,1,87,1,88,1,88,1,88,1,88,1,88,1,88,1,89,1,89,1,89,1, + 89,1,89,1,89,1,89,1,90,1,90,1,90,1,90,1,90,1,90,1,90,1,91,1,91,1, + 91,1,91,1,91,1,91,1,91,1,92,1,92,1,92,1,92,1,92,1,93,1,93,1,93,1, + 93,1,93,1,93,1,94,1,94,1,94,1,94,1,94,1,95,1,95,1,95,1,96,1,96,1, + 97,1,97,1,98,1,98,1,99,1,99,1,100,1,100,1,101,1,101,1,102,1,102, + 1,103,1,103,1,104,1,104,1,105,1,105,1,106,1,106,1,107,1,107,1,108, + 1,108,1,109,1,109,1,110,3,110,1011,8,110,1,110,4,110,1014,8,110, + 11,110,12,110,1015,1,111,3,111,1019,8,111,1,111,1,111,1,111,5,111, + 1024,8,111,10,111,12,111,1027,9,111,3,111,1029,8,111,1,111,1,111, + 4,111,1033,8,111,11,111,12,111,1034,3,111,1037,8,111,1,111,1,111, + 3,111,1041,8,111,1,111,4,111,1044,8,111,11,111,12,111,1045,3,111, + 1048,8,111,1,112,1,112,5,112,1052,8,112,10,112,12,112,1055,9,112, + 1,113,1,113,1,113,5,113,1060,8,113,10,113,12,113,1063,9,113,1,113, + 1,113,1,114,1,114,1,114,1,114,1,114,1,114,1,114,1,114,3,114,1075, + 8,114,1,115,1,115,1,116,1,116,1,116,1,116,5,116,1083,8,116,10,116, + 12,116,1086,9,116,1,116,1,116,1,117,1,117,1,117,1,117,5,117,1094, + 8,117,10,117,12,117,1097,9,117,1,117,1,117,1,117,1,117,1,117,1,118, + 4,118,1105,8,118,11,118,12,118,1106,1,118,1,118,1,1095,0,119,1,1, + 3,2,5,3,7,4,9,5,11,6,13,7,15,8,17,9,19,10,21,11,23,12,25,13,27,14, + 29,15,31,16,33,17,35,18,37,19,39,20,41,21,43,22,45,23,47,24,49,25, + 51,26,53,27,55,28,57,29,59,30,61,31,63,32,65,33,67,34,69,35,71,36, + 73,37,75,38,77,39,79,40,81,41,83,42,85,43,87,44,89,45,91,46,93,47, + 95,48,97,49,99,50,101,51,103,52,105,53,107,54,109,55,111,56,113, + 57,115,58,117,59,119,60,121,61,123,62,125,63,127,64,129,65,131,66, + 133,67,135,68,137,69,139,70,141,71,143,72,145,73,147,74,149,75,151, + 76,153,77,155,78,157,79,159,80,161,81,163,82,165,83,167,84,169,85, + 171,86,173,87,175,88,177,89,179,90,181,91,183,92,185,93,187,94,189, + 95,191,96,193,97,195,98,197,99,199,100,201,101,203,102,205,103,207, + 104,209,105,211,106,213,107,215,108,217,109,219,110,221,111,223, + 112,225,113,227,114,229,0,231,0,233,115,235,116,237,117,1,0,11,1, + 0,48,57,1,0,49,57,2,0,69,69,101,101,2,0,43,43,45,45,3,0,65,90,95, + 95,97,122,4,0,48,57,65,90,95,95,97,122,4,0,10,10,13,13,34,34,92, + 92,8,0,34,34,47,47,92,92,98,98,102,102,110,110,114,114,116,116,3, + 0,48,57,65,70,97,102,2,0,10,10,13,13,3,0,9,10,13,13,32,32,1124,0, + 1,1,0,0,0,0,3,1,0,0,0,0,5,1,0,0,0,0,7,1,0,0,0,0,9,1,0,0,0,0,11,1, + 0,0,0,0,13,1,0,0,0,0,15,1,0,0,0,0,17,1,0,0,0,0,19,1,0,0,0,0,21,1, + 0,0,0,0,23,1,0,0,0,0,25,1,0,0,0,0,27,1,0,0,0,0,29,1,0,0,0,0,31,1, + 0,0,0,0,33,1,0,0,0,0,35,1,0,0,0,0,37,1,0,0,0,0,39,1,0,0,0,0,41,1, + 0,0,0,0,43,1,0,0,0,0,45,1,0,0,0,0,47,1,0,0,0,0,49,1,0,0,0,0,51,1, + 0,0,0,0,53,1,0,0,0,0,55,1,0,0,0,0,57,1,0,0,0,0,59,1,0,0,0,0,61,1, + 0,0,0,0,63,1,0,0,0,0,65,1,0,0,0,0,67,1,0,0,0,0,69,1,0,0,0,0,71,1, + 0,0,0,0,73,1,0,0,0,0,75,1,0,0,0,0,77,1,0,0,0,0,79,1,0,0,0,0,81,1, + 0,0,0,0,83,1,0,0,0,0,85,1,0,0,0,0,87,1,0,0,0,0,89,1,0,0,0,0,91,1, + 0,0,0,0,93,1,0,0,0,0,95,1,0,0,0,0,97,1,0,0,0,0,99,1,0,0,0,0,101, + 1,0,0,0,0,103,1,0,0,0,0,105,1,0,0,0,0,107,1,0,0,0,0,109,1,0,0,0, + 0,111,1,0,0,0,0,113,1,0,0,0,0,115,1,0,0,0,0,117,1,0,0,0,0,119,1, + 0,0,0,0,121,1,0,0,0,0,123,1,0,0,0,0,125,1,0,0,0,0,127,1,0,0,0,0, + 129,1,0,0,0,0,131,1,0,0,0,0,133,1,0,0,0,0,135,1,0,0,0,0,137,1,0, + 0,0,0,139,1,0,0,0,0,141,1,0,0,0,0,143,1,0,0,0,0,145,1,0,0,0,0,147, + 1,0,0,0,0,149,1,0,0,0,0,151,1,0,0,0,0,153,1,0,0,0,0,155,1,0,0,0, + 0,157,1,0,0,0,0,159,1,0,0,0,0,161,1,0,0,0,0,163,1,0,0,0,0,165,1, + 0,0,0,0,167,1,0,0,0,0,169,1,0,0,0,0,171,1,0,0,0,0,173,1,0,0,0,0, + 175,1,0,0,0,0,177,1,0,0,0,0,179,1,0,0,0,0,181,1,0,0,0,0,183,1,0, + 0,0,0,185,1,0,0,0,0,187,1,0,0,0,0,189,1,0,0,0,0,191,1,0,0,0,0,193, + 1,0,0,0,0,195,1,0,0,0,0,197,1,0,0,0,0,199,1,0,0,0,0,201,1,0,0,0, + 0,203,1,0,0,0,0,205,1,0,0,0,0,207,1,0,0,0,0,209,1,0,0,0,0,211,1, + 0,0,0,0,213,1,0,0,0,0,215,1,0,0,0,0,217,1,0,0,0,0,219,1,0,0,0,0, + 221,1,0,0,0,0,223,1,0,0,0,0,225,1,0,0,0,0,227,1,0,0,0,0,233,1,0, + 0,0,0,235,1,0,0,0,0,237,1,0,0,0,1,239,1,0,0,0,3,249,1,0,0,0,5,254, + 1,0,0,0,7,261,1,0,0,0,9,270,1,0,0,0,11,281,1,0,0,0,13,285,1,0,0, + 0,15,294,1,0,0,0,17,301,1,0,0,0,19,310,1,0,0,0,21,315,1,0,0,0,23, + 325,1,0,0,0,25,336,1,0,0,0,27,344,1,0,0,0,29,350,1,0,0,0,31,359, + 1,0,0,0,33,369,1,0,0,0,35,378,1,0,0,0,37,390,1,0,0,0,39,401,1,0, + 0,0,41,407,1,0,0,0,43,415,1,0,0,0,45,418,1,0,0,0,47,423,1,0,0,0, + 49,426,1,0,0,0,51,434,1,0,0,0,53,441,1,0,0,0,55,447,1,0,0,0,57,452, + 1,0,0,0,59,463,1,0,0,0,61,468,1,0,0,0,63,474,1,0,0,0,65,478,1,0, + 0,0,67,490,1,0,0,0,69,493,1,0,0,0,71,500,1,0,0,0,73,503,1,0,0,0, + 75,510,1,0,0,0,77,518,1,0,0,0,79,525,1,0,0,0,81,536,1,0,0,0,83,543, + 1,0,0,0,85,552,1,0,0,0,87,567,1,0,0,0,89,577,1,0,0,0,91,590,1,0, + 0,0,93,596,1,0,0,0,95,613,1,0,0,0,97,616,1,0,0,0,99,620,1,0,0,0, + 101,625,1,0,0,0,103,631,1,0,0,0,105,640,1,0,0,0,107,649,1,0,0,0, + 109,653,1,0,0,0,111,657,1,0,0,0,113,661,1,0,0,0,115,667,1,0,0,0, + 117,673,1,0,0,0,119,678,1,0,0,0,121,683,1,0,0,0,123,689,1,0,0,0, + 125,697,1,0,0,0,127,705,1,0,0,0,129,716,1,0,0,0,131,721,1,0,0,0, + 133,733,1,0,0,0,135,744,1,0,0,0,137,754,1,0,0,0,139,766,1,0,0,0, + 141,786,1,0,0,0,143,791,1,0,0,0,145,804,1,0,0,0,147,816,1,0,0,0, + 149,828,1,0,0,0,151,833,1,0,0,0,153,841,1,0,0,0,155,846,1,0,0,0, + 157,859,1,0,0,0,159,867,1,0,0,0,161,876,1,0,0,0,163,890,1,0,0,0, + 165,899,1,0,0,0,167,904,1,0,0,0,169,911,1,0,0,0,171,916,1,0,0,0, + 173,922,1,0,0,0,175,929,1,0,0,0,177,935,1,0,0,0,179,941,1,0,0,0, + 181,948,1,0,0,0,183,955,1,0,0,0,185,962,1,0,0,0,187,967,1,0,0,0, + 189,973,1,0,0,0,191,978,1,0,0,0,193,981,1,0,0,0,195,983,1,0,0,0, + 197,985,1,0,0,0,199,987,1,0,0,0,201,989,1,0,0,0,203,991,1,0,0,0, + 205,993,1,0,0,0,207,995,1,0,0,0,209,997,1,0,0,0,211,999,1,0,0,0, + 213,1001,1,0,0,0,215,1003,1,0,0,0,217,1005,1,0,0,0,219,1007,1,0, + 0,0,221,1010,1,0,0,0,223,1018,1,0,0,0,225,1049,1,0,0,0,227,1056, + 1,0,0,0,229,1066,1,0,0,0,231,1076,1,0,0,0,233,1078,1,0,0,0,235,1089, + 1,0,0,0,237,1104,1,0,0,0,239,240,5,119,0,0,240,241,5,111,0,0,241, + 242,5,114,0,0,242,243,5,107,0,0,243,244,5,115,0,0,244,245,5,112, + 0,0,245,246,5,97,0,0,246,247,5,99,0,0,247,248,5,101,0,0,248,2,1, + 0,0,0,249,250,5,116,0,0,250,251,5,121,0,0,251,252,5,112,0,0,252, + 253,5,101,0,0,253,4,1,0,0,0,254,255,5,111,0,0,255,256,5,98,0,0,256, + 257,5,106,0,0,257,258,5,101,0,0,258,259,5,99,0,0,259,260,5,116,0, + 0,260,6,1,0,0,0,261,262,5,115,0,0,262,263,5,116,0,0,263,264,5,111, + 0,0,264,265,5,114,0,0,265,266,5,97,0,0,266,267,5,98,0,0,267,268, + 5,108,0,0,268,269,5,101,0,0,269,8,1,0,0,0,270,271,5,105,0,0,271, + 272,5,109,0,0,272,273,5,112,0,0,273,274,5,108,0,0,274,275,5,101, + 0,0,275,276,5,109,0,0,276,277,5,101,0,0,277,278,5,110,0,0,278,279, + 5,116,0,0,279,280,5,115,0,0,280,10,1,0,0,0,281,282,5,114,0,0,282, + 283,5,101,0,0,283,284,5,102,0,0,284,12,1,0,0,0,285,286,5,102,0,0, + 286,287,5,114,0,0,287,288,5,97,0,0,288,289,5,103,0,0,289,290,5,109, + 0,0,290,291,5,101,0,0,291,292,5,110,0,0,292,293,5,116,0,0,293,14, + 1,0,0,0,294,295,5,105,0,0,295,296,5,109,0,0,296,297,5,112,0,0,297, + 298,5,111,0,0,298,299,5,114,0,0,299,300,5,116,0,0,300,16,1,0,0,0, + 301,302,5,101,0,0,302,303,5,120,0,0,303,304,5,116,0,0,304,305,5, + 101,0,0,305,306,5,114,0,0,306,307,5,110,0,0,307,308,5,97,0,0,308, + 309,5,108,0,0,309,18,1,0,0,0,310,311,5,97,0,0,311,312,5,116,0,0, + 312,313,5,111,0,0,313,314,5,109,0,0,314,20,1,0,0,0,315,316,5,105, + 0,0,316,317,5,110,0,0,317,318,5,116,0,0,318,319,5,101,0,0,319,320, + 5,114,0,0,320,321,5,102,0,0,321,322,5,97,0,0,322,323,5,99,0,0,323, + 324,5,101,0,0,324,22,1,0,0,0,325,326,5,105,0,0,326,327,5,110,0,0, + 327,328,5,116,0,0,328,329,5,101,0,0,329,330,5,114,0,0,330,331,5, + 102,0,0,331,332,5,97,0,0,332,333,5,99,0,0,333,334,5,101,0,0,334, + 335,5,115,0,0,335,24,1,0,0,0,336,337,5,112,0,0,337,338,5,97,0,0, + 338,339,5,99,0,0,339,340,5,107,0,0,340,341,5,97,0,0,341,342,5,103, + 0,0,342,343,5,101,0,0,343,26,1,0,0,0,344,345,5,118,0,0,345,346,5, + 97,0,0,346,347,5,108,0,0,347,348,5,117,0,0,348,349,5,101,0,0,349, + 28,1,0,0,0,350,351,5,114,0,0,351,352,5,101,0,0,352,353,5,108,0,0, + 353,354,5,97,0,0,354,355,5,116,0,0,355,356,5,105,0,0,356,357,5,111, + 0,0,357,358,5,110,0,0,358,30,1,0,0,0,359,360,5,111,0,0,360,361,5, + 112,0,0,361,362,5,101,0,0,362,363,5,114,0,0,363,364,5,97,0,0,364, + 365,5,116,0,0,365,366,5,105,0,0,366,367,5,111,0,0,367,368,5,110, + 0,0,368,32,1,0,0,0,369,370,5,102,0,0,370,371,5,117,0,0,371,372,5, + 110,0,0,372,373,5,99,0,0,373,374,5,116,0,0,374,375,5,105,0,0,375, + 376,5,111,0,0,376,377,5,110,0,0,377,34,1,0,0,0,378,379,5,99,0,0, + 379,380,5,111,0,0,380,381,5,110,0,0,381,382,5,115,0,0,382,383,5, + 116,0,0,383,384,5,114,0,0,384,385,5,117,0,0,385,386,5,99,0,0,386, + 387,5,116,0,0,387,388,5,111,0,0,388,389,5,114,0,0,389,36,1,0,0,0, + 390,391,5,99,0,0,391,392,5,111,0,0,392,393,5,110,0,0,393,394,5,115, + 0,0,394,395,5,116,0,0,395,396,5,114,0,0,396,397,5,117,0,0,397,398, + 5,99,0,0,398,399,5,116,0,0,399,400,5,115,0,0,400,38,1,0,0,0,401, + 402,5,105,0,0,402,403,5,110,0,0,403,404,5,112,0,0,404,405,5,117, + 0,0,405,406,5,116,0,0,406,40,1,0,0,0,407,408,5,99,0,0,408,409,5, + 111,0,0,409,410,5,110,0,0,410,411,5,102,0,0,411,412,5,111,0,0,412, + 413,5,114,0,0,413,414,5,109,0,0,414,42,1,0,0,0,415,416,5,97,0,0, + 416,417,5,115,0,0,417,44,1,0,0,0,418,419,5,98,0,0,419,420,5,105, + 0,0,420,421,5,110,0,0,421,422,5,100,0,0,422,46,1,0,0,0,423,424,5, + 116,0,0,424,425,5,111,0,0,425,48,1,0,0,0,426,427,5,112,0,0,427,428, + 5,114,0,0,428,429,5,105,0,0,429,430,5,118,0,0,430,431,5,97,0,0,431, + 432,5,116,0,0,432,433,5,101,0,0,433,50,1,0,0,0,434,435,5,115,0,0, + 435,436,5,104,0,0,436,437,5,97,0,0,437,438,5,114,0,0,438,439,5,101, + 0,0,439,440,5,100,0,0,440,52,1,0,0,0,441,442,5,115,0,0,442,443,5, + 116,0,0,443,444,5,97,0,0,444,445,5,116,0,0,445,446,5,101,0,0,446, + 54,1,0,0,0,447,448,5,101,0,0,448,449,5,100,0,0,449,450,5,103,0,0, + 450,451,5,101,0,0,451,56,1,0,0,0,452,453,5,112,0,0,453,454,5,114, + 0,0,454,455,5,111,0,0,455,456,5,106,0,0,456,457,5,101,0,0,457,458, + 5,99,0,0,458,459,5,116,0,0,459,460,5,105,0,0,460,461,5,111,0,0,461, + 462,5,110,0,0,462,58,1,0,0,0,463,464,5,119,0,0,464,465,5,105,0,0, + 465,466,5,116,0,0,466,467,5,104,0,0,467,60,1,0,0,0,468,469,5,117, + 0,0,469,470,5,115,0,0,470,471,5,105,0,0,471,472,5,110,0,0,472,473, + 5,103,0,0,473,62,1,0,0,0,474,475,5,118,0,0,475,476,5,105,0,0,476, + 477,5,97,0,0,477,64,1,0,0,0,478,479,5,109,0,0,479,480,5,97,0,0,480, + 481,5,116,0,0,481,482,5,101,0,0,482,483,5,114,0,0,483,484,5,105, + 0,0,484,485,5,97,0,0,485,486,5,108,0,0,486,487,5,105,0,0,487,488, + 5,122,0,0,488,489,5,101,0,0,489,66,1,0,0,0,490,491,5,105,0,0,491, + 492,5,102,0,0,492,68,1,0,0,0,493,494,5,97,0,0,494,495,5,98,0,0,495, + 496,5,115,0,0,496,497,5,101,0,0,497,498,5,110,0,0,498,499,5,116, + 0,0,499,70,1,0,0,0,500,501,5,111,0,0,501,502,5,110,0,0,502,72,1, + 0,0,0,503,504,5,112,0,0,504,505,5,111,0,0,505,506,5,108,0,0,506, + 507,5,105,0,0,507,508,5,99,0,0,508,509,5,121,0,0,509,74,1,0,0,0, + 510,511,5,100,0,0,511,512,5,101,0,0,512,513,5,102,0,0,513,514,5, + 97,0,0,514,515,5,117,0,0,515,516,5,108,0,0,516,517,5,116,0,0,517, + 76,1,0,0,0,518,519,5,115,0,0,519,520,5,111,0,0,520,521,5,117,0,0, + 521,522,5,114,0,0,522,523,5,99,0,0,523,524,5,101,0,0,524,78,1,0, + 0,0,525,526,5,114,0,0,526,527,5,101,0,0,527,528,5,112,0,0,528,529, + 5,111,0,0,529,530,5,115,0,0,530,531,5,105,0,0,531,532,5,116,0,0, + 532,533,5,111,0,0,533,534,5,114,0,0,534,535,5,121,0,0,535,80,1,0, + 0,0,536,537,5,99,0,0,537,538,5,111,0,0,538,539,5,109,0,0,539,540, + 5,109,0,0,540,541,5,105,0,0,541,542,5,116,0,0,542,82,1,0,0,0,543, + 544,5,114,0,0,544,545,5,101,0,0,545,546,5,118,0,0,546,547,5,105, + 0,0,547,548,5,115,0,0,548,549,5,105,0,0,549,550,5,111,0,0,550,551, + 5,110,0,0,551,84,1,0,0,0,552,553,5,115,0,0,553,554,5,101,0,0,554, + 555,5,109,0,0,555,556,5,97,0,0,556,557,5,110,0,0,557,558,5,116,0, + 0,558,559,5,105,0,0,559,560,5,99,0,0,560,561,5,45,0,0,561,562,5, + 109,0,0,562,563,5,97,0,0,563,564,5,106,0,0,564,565,5,111,0,0,565, + 566,5,114,0,0,566,86,1,0,0,0,567,568,5,111,0,0,568,569,5,110,0,0, + 569,570,5,45,0,0,570,571,5,100,0,0,571,572,5,101,0,0,572,573,5,108, + 0,0,573,574,5,101,0,0,574,575,5,116,0,0,575,576,5,101,0,0,576,88, + 1,0,0,0,577,578,5,114,0,0,578,579,5,101,0,0,579,580,5,116,0,0,580, + 581,5,97,0,0,581,582,5,105,0,0,582,583,5,110,0,0,583,584,5,45,0, + 0,584,585,5,111,0,0,585,586,5,116,0,0,586,587,5,104,0,0,587,588, + 5,101,0,0,588,589,5,114,0,0,589,90,1,0,0,0,590,591,5,107,0,0,591, + 592,5,101,0,0,592,593,5,121,0,0,593,594,5,101,0,0,594,595,5,100, + 0,0,595,92,1,0,0,0,596,597,5,112,0,0,597,598,5,117,0,0,598,599,5, + 98,0,0,599,600,5,108,0,0,600,601,5,105,0,0,601,602,5,99,0,0,602, + 603,5,45,0,0,603,604,5,116,0,0,604,605,5,114,0,0,605,606,5,97,0, + 0,606,607,5,118,0,0,607,608,5,101,0,0,608,609,5,114,0,0,609,610, + 5,115,0,0,610,611,5,97,0,0,611,612,5,108,0,0,612,94,1,0,0,0,613, + 614,5,105,0,0,614,615,5,100,0,0,615,96,1,0,0,0,616,617,5,100,0,0, + 617,618,5,111,0,0,618,619,5,99,0,0,619,98,1,0,0,0,620,621,5,109, + 0,0,621,622,5,111,0,0,622,623,5,100,0,0,623,624,5,101,0,0,624,100, + 1,0,0,0,625,626,5,101,0,0,626,627,5,109,0,0,627,628,5,105,0,0,628, + 629,5,116,0,0,629,630,5,115,0,0,630,102,1,0,0,0,631,632,5,114,0, + 0,632,633,5,101,0,0,633,634,5,99,0,0,634,635,5,101,0,0,635,636,5, + 105,0,0,636,637,5,118,0,0,637,638,5,101,0,0,638,639,5,114,0,0,639, + 104,1,0,0,0,640,641,5,114,0,0,641,642,5,101,0,0,642,643,5,113,0, + 0,643,644,5,117,0,0,644,645,5,105,0,0,645,646,5,114,0,0,646,647, + 5,101,0,0,647,648,5,115,0,0,648,106,1,0,0,0,649,650,5,97,0,0,650, + 651,5,110,0,0,651,652,5,121,0,0,652,108,1,0,0,0,653,654,5,103,0, + 0,654,655,5,101,0,0,655,656,5,116,0,0,656,110,1,0,0,0,657,658,5, + 115,0,0,658,659,5,101,0,0,659,660,5,116,0,0,660,112,1,0,0,0,661, + 662,5,119,0,0,662,663,5,97,0,0,663,664,5,116,0,0,664,665,5,99,0, + 0,665,666,5,104,0,0,666,114,1,0,0,0,667,668,5,115,0,0,668,669,5, + 116,0,0,669,670,5,97,0,0,670,671,5,114,0,0,671,672,5,116,0,0,672, + 116,1,0,0,0,673,674,5,115,0,0,674,675,5,116,0,0,675,676,5,111,0, + 0,676,677,5,112,0,0,677,118,1,0,0,0,678,679,5,114,0,0,679,680,5, + 101,0,0,680,681,5,97,0,0,681,682,5,100,0,0,682,120,1,0,0,0,683,684, + 5,119,0,0,684,685,5,114,0,0,685,686,5,105,0,0,686,687,5,116,0,0, + 687,688,5,101,0,0,688,122,1,0,0,0,689,690,5,114,0,0,690,691,5,101, + 0,0,691,692,5,115,0,0,692,693,5,111,0,0,693,694,5,108,0,0,694,695, + 5,118,0,0,695,696,5,101,0,0,696,124,1,0,0,0,697,698,5,99,0,0,698, + 699,5,111,0,0,699,700,5,110,0,0,700,701,5,110,0,0,701,702,5,101, + 0,0,702,703,5,99,0,0,703,704,5,116,0,0,704,126,1,0,0,0,705,706,5, + 100,0,0,706,707,5,105,0,0,707,708,5,115,0,0,708,709,5,99,0,0,709, + 710,5,111,0,0,710,711,5,110,0,0,711,712,5,110,0,0,712,713,5,101, + 0,0,713,714,5,99,0,0,714,715,5,116,0,0,715,128,1,0,0,0,716,717,5, + 99,0,0,717,718,5,97,0,0,718,719,5,108,0,0,719,720,5,108,0,0,720, + 130,1,0,0,0,721,722,5,119,0,0,722,723,5,97,0,0,723,724,5,116,0,0, + 724,725,5,99,0,0,725,726,5,104,0,0,726,727,5,45,0,0,727,728,5,115, + 0,0,728,729,5,116,0,0,729,730,5,97,0,0,730,731,5,114,0,0,731,732, + 5,116,0,0,732,132,1,0,0,0,733,734,5,119,0,0,734,735,5,97,0,0,735, + 736,5,116,0,0,736,737,5,99,0,0,737,738,5,104,0,0,738,739,5,45,0, + 0,739,740,5,115,0,0,740,741,5,116,0,0,741,742,5,111,0,0,742,743, + 5,112,0,0,743,134,1,0,0,0,744,745,5,115,0,0,745,746,5,117,0,0,746, + 747,5,98,0,0,747,748,5,115,0,0,748,749,5,99,0,0,749,750,5,114,0, + 0,750,751,5,105,0,0,751,752,5,98,0,0,752,753,5,101,0,0,753,136,1, + 0,0,0,754,755,5,117,0,0,755,756,5,110,0,0,756,757,5,115,0,0,757, + 758,5,117,0,0,758,759,5,98,0,0,759,760,5,115,0,0,760,761,5,99,0, + 0,761,762,5,114,0,0,762,763,5,105,0,0,763,764,5,98,0,0,764,765,5, + 101,0,0,765,138,1,0,0,0,766,767,5,111,0,0,767,768,5,112,0,0,768, + 769,5,116,0,0,769,770,5,105,0,0,770,771,5,109,0,0,771,772,5,105, + 0,0,772,773,5,115,0,0,773,774,5,116,0,0,774,775,5,105,0,0,775,776, + 5,99,0,0,776,777,5,45,0,0,777,778,5,114,0,0,778,779,5,101,0,0,779, + 780,5,103,0,0,780,781,5,105,0,0,781,782,5,115,0,0,782,783,5,116, + 0,0,783,784,5,101,0,0,784,785,5,114,0,0,785,140,1,0,0,0,786,787, + 5,99,0,0,787,788,5,114,0,0,788,789,5,100,0,0,789,790,5,116,0,0,790, + 142,1,0,0,0,791,792,5,111,0,0,792,793,5,112,0,0,793,794,5,116,0, + 0,794,795,5,105,0,0,795,796,5,111,0,0,796,797,5,110,0,0,797,798, + 5,97,0,0,798,799,5,108,0,0,799,800,5,45,0,0,800,801,5,111,0,0,801, + 802,5,110,0,0,802,803,5,101,0,0,803,144,1,0,0,0,804,805,5,101,0, + 0,805,806,5,120,0,0,806,807,5,97,0,0,807,808,5,99,0,0,808,809,5, + 116,0,0,809,810,5,108,0,0,810,811,5,121,0,0,811,812,5,45,0,0,812, + 813,5,111,0,0,813,814,5,110,0,0,814,815,5,101,0,0,815,146,1,0,0, + 0,816,817,5,109,0,0,817,818,5,97,0,0,818,819,5,110,0,0,819,820,5, + 121,0,0,820,821,5,45,0,0,821,822,5,117,0,0,822,823,5,110,0,0,823, + 824,5,105,0,0,824,825,5,113,0,0,825,826,5,117,0,0,826,827,5,101, + 0,0,827,148,1,0,0,0,828,829,5,109,0,0,829,830,5,97,0,0,830,831,5, + 110,0,0,831,832,5,121,0,0,832,150,1,0,0,0,833,834,5,111,0,0,834, + 835,5,114,0,0,835,836,5,100,0,0,836,837,5,101,0,0,837,838,5,114, + 0,0,838,839,5,101,0,0,839,840,5,100,0,0,840,152,1,0,0,0,841,842, + 5,117,0,0,842,843,5,110,0,0,843,844,5,105,0,0,844,845,5,116,0,0, + 845,154,1,0,0,0,846,847,5,119,0,0,847,848,5,97,0,0,848,849,5,116, + 0,0,849,850,5,99,0,0,850,851,5,104,0,0,851,852,5,45,0,0,852,853, + 5,104,0,0,853,854,5,97,0,0,854,855,5,110,0,0,855,856,5,100,0,0,856, + 857,5,108,0,0,857,858,5,101,0,0,858,156,1,0,0,0,859,860,5,109,0, + 0,860,861,5,101,0,0,861,862,5,115,0,0,862,863,5,115,0,0,863,864, + 5,97,0,0,864,865,5,103,0,0,865,866,5,101,0,0,866,158,1,0,0,0,867, + 868,5,97,0,0,868,869,5,116,0,0,869,870,5,111,0,0,870,871,5,109,0, + 0,871,872,5,45,0,0,872,873,5,114,0,0,873,874,5,101,0,0,874,875,5, + 102,0,0,875,160,1,0,0,0,876,877,5,105,0,0,877,878,5,110,0,0,878, + 879,5,116,0,0,879,880,5,101,0,0,880,881,5,114,0,0,881,882,5,102, + 0,0,882,883,5,97,0,0,883,884,5,99,0,0,884,885,5,101,0,0,885,886, + 5,45,0,0,886,887,5,114,0,0,887,888,5,101,0,0,888,889,5,102,0,0,889, + 162,1,0,0,0,890,891,5,111,0,0,891,892,5,112,0,0,892,893,5,116,0, + 0,893,894,5,105,0,0,894,895,5,111,0,0,895,896,5,110,0,0,896,897, + 5,97,0,0,897,898,5,108,0,0,898,164,1,0,0,0,899,900,5,108,0,0,900, + 901,5,105,0,0,901,902,5,115,0,0,902,903,5,116,0,0,903,166,1,0,0, + 0,904,905,5,114,0,0,905,906,5,101,0,0,906,907,5,99,0,0,907,908,5, + 111,0,0,908,909,5,114,0,0,909,910,5,100,0,0,910,168,1,0,0,0,911, + 912,5,98,0,0,912,913,5,111,0,0,913,914,5,111,0,0,914,915,5,108,0, + 0,915,170,1,0,0,0,916,917,5,98,0,0,917,918,5,121,0,0,918,919,5,116, + 0,0,919,920,5,101,0,0,920,921,5,115,0,0,921,172,1,0,0,0,922,923, + 5,100,0,0,923,924,5,111,0,0,924,925,5,117,0,0,925,926,5,98,0,0,926, + 927,5,108,0,0,927,928,5,101,0,0,928,174,1,0,0,0,929,930,5,105,0, + 0,930,931,5,110,0,0,931,932,5,116,0,0,932,933,5,51,0,0,933,934,5, + 50,0,0,934,176,1,0,0,0,935,936,5,105,0,0,936,937,5,110,0,0,937,938, + 5,116,0,0,938,939,5,54,0,0,939,940,5,52,0,0,940,178,1,0,0,0,941, + 942,5,115,0,0,942,943,5,116,0,0,943,944,5,114,0,0,944,945,5,105, + 0,0,945,946,5,110,0,0,946,947,5,103,0,0,947,180,1,0,0,0,948,949, + 5,117,0,0,949,950,5,105,0,0,950,951,5,110,0,0,951,952,5,116,0,0, + 952,953,5,51,0,0,953,954,5,50,0,0,954,182,1,0,0,0,955,956,5,117, + 0,0,956,957,5,105,0,0,957,958,5,110,0,0,958,959,5,116,0,0,959,960, + 5,54,0,0,960,961,5,52,0,0,961,184,1,0,0,0,962,963,5,116,0,0,963, + 964,5,114,0,0,964,965,5,117,0,0,965,966,5,101,0,0,966,186,1,0,0, + 0,967,968,5,102,0,0,968,969,5,97,0,0,969,970,5,108,0,0,970,971,5, + 115,0,0,971,972,5,101,0,0,972,188,1,0,0,0,973,974,5,110,0,0,974, + 975,5,117,0,0,975,976,5,108,0,0,976,977,5,108,0,0,977,190,1,0,0, + 0,978,979,5,45,0,0,979,980,5,62,0,0,980,192,1,0,0,0,981,982,5,58, + 0,0,982,194,1,0,0,0,983,984,5,59,0,0,984,196,1,0,0,0,985,986,5,44, + 0,0,986,198,1,0,0,0,987,988,5,46,0,0,988,200,1,0,0,0,989,990,5,123, + 0,0,990,202,1,0,0,0,991,992,5,125,0,0,992,204,1,0,0,0,993,994,5, + 91,0,0,994,206,1,0,0,0,995,996,5,93,0,0,996,208,1,0,0,0,997,998, + 5,40,0,0,998,210,1,0,0,0,999,1000,5,41,0,0,1000,212,1,0,0,0,1001, + 1002,5,60,0,0,1002,214,1,0,0,0,1003,1004,5,62,0,0,1004,216,1,0,0, + 0,1005,1006,5,38,0,0,1006,218,1,0,0,0,1007,1008,5,61,0,0,1008,220, + 1,0,0,0,1009,1011,5,45,0,0,1010,1009,1,0,0,0,1010,1011,1,0,0,0,1011, + 1013,1,0,0,0,1012,1014,7,0,0,0,1013,1012,1,0,0,0,1014,1015,1,0,0, + 0,1015,1013,1,0,0,0,1015,1016,1,0,0,0,1016,222,1,0,0,0,1017,1019, + 5,45,0,0,1018,1017,1,0,0,0,1018,1019,1,0,0,0,1019,1028,1,0,0,0,1020, + 1029,5,48,0,0,1021,1025,7,1,0,0,1022,1024,7,0,0,0,1023,1022,1,0, + 0,0,1024,1027,1,0,0,0,1025,1023,1,0,0,0,1025,1026,1,0,0,0,1026,1029, + 1,0,0,0,1027,1025,1,0,0,0,1028,1020,1,0,0,0,1028,1021,1,0,0,0,1029, + 1036,1,0,0,0,1030,1032,5,46,0,0,1031,1033,7,0,0,0,1032,1031,1,0, + 0,0,1033,1034,1,0,0,0,1034,1032,1,0,0,0,1034,1035,1,0,0,0,1035,1037, + 1,0,0,0,1036,1030,1,0,0,0,1036,1037,1,0,0,0,1037,1047,1,0,0,0,1038, + 1040,7,2,0,0,1039,1041,7,3,0,0,1040,1039,1,0,0,0,1040,1041,1,0,0, + 0,1041,1043,1,0,0,0,1042,1044,7,0,0,0,1043,1042,1,0,0,0,1044,1045, + 1,0,0,0,1045,1043,1,0,0,0,1045,1046,1,0,0,0,1046,1048,1,0,0,0,1047, + 1038,1,0,0,0,1047,1048,1,0,0,0,1048,224,1,0,0,0,1049,1053,7,4,0, + 0,1050,1052,7,5,0,0,1051,1050,1,0,0,0,1052,1055,1,0,0,0,1053,1051, + 1,0,0,0,1053,1054,1,0,0,0,1054,226,1,0,0,0,1055,1053,1,0,0,0,1056, + 1061,5,34,0,0,1057,1060,3,229,114,0,1058,1060,8,6,0,0,1059,1057, + 1,0,0,0,1059,1058,1,0,0,0,1060,1063,1,0,0,0,1061,1059,1,0,0,0,1061, + 1062,1,0,0,0,1062,1064,1,0,0,0,1063,1061,1,0,0,0,1064,1065,5,34, + 0,0,1065,228,1,0,0,0,1066,1074,5,92,0,0,1067,1075,7,7,0,0,1068,1069, + 5,117,0,0,1069,1070,3,231,115,0,1070,1071,3,231,115,0,1071,1072, + 3,231,115,0,1072,1073,3,231,115,0,1073,1075,1,0,0,0,1074,1067,1, + 0,0,0,1074,1068,1,0,0,0,1075,230,1,0,0,0,1076,1077,7,8,0,0,1077, + 232,1,0,0,0,1078,1079,5,47,0,0,1079,1080,5,47,0,0,1080,1084,1,0, + 0,0,1081,1083,8,9,0,0,1082,1081,1,0,0,0,1083,1086,1,0,0,0,1084,1082, + 1,0,0,0,1084,1085,1,0,0,0,1085,1087,1,0,0,0,1086,1084,1,0,0,0,1087, + 1088,6,116,0,0,1088,234,1,0,0,0,1089,1090,5,47,0,0,1090,1091,5,42, + 0,0,1091,1095,1,0,0,0,1092,1094,9,0,0,0,1093,1092,1,0,0,0,1094,1097, + 1,0,0,0,1095,1096,1,0,0,0,1095,1093,1,0,0,0,1096,1098,1,0,0,0,1097, + 1095,1,0,0,0,1098,1099,5,42,0,0,1099,1100,5,47,0,0,1100,1101,1,0, + 0,0,1101,1102,6,117,0,0,1102,236,1,0,0,0,1103,1105,7,10,0,0,1104, + 1103,1,0,0,0,1105,1106,1,0,0,0,1106,1104,1,0,0,0,1106,1107,1,0,0, + 0,1107,1108,1,0,0,0,1108,1109,6,118,0,0,1109,238,1,0,0,0,18,0,1010, + 1015,1018,1025,1028,1034,1036,1040,1045,1047,1053,1059,1061,1074, + 1084,1095,1106,1,0,1,0 ]; private static __ATN: antlr.ATN; diff --git a/src/capability-language/generated/QuixosCapabilityParser.ts b/src/capability-language/generated/QuixosCapabilityParser.ts index 791a471..2ab5291 100644 --- a/src/capability-language/generated/QuixosCapabilityParser.ts +++ b/src/capability-language/generated/QuixosCapabilityParser.ts @@ -11,115 +11,122 @@ type int = number; export class QuixosCapabilityParser extends antlr.Parser { public static readonly WORKSPACE = 1; - public static readonly FRAGMENT = 2; - public static readonly IMPORT = 3; - public static readonly EXTERNAL = 4; - public static readonly ATOM = 5; - public static readonly INTERFACE = 6; - public static readonly INTERFACES = 7; - public static readonly PACKAGE = 8; - public static readonly VALUE = 9; - public static readonly RELATION = 10; - public static readonly OPERATION = 11; - public static readonly FUNCTION = 12; - public static readonly CONSTRUCTOR = 13; - public static readonly CONSTRUCTS = 14; - public static readonly INPUT = 15; - public static readonly CONFORM = 16; - public static readonly AS = 17; - public static readonly BIND = 18; - public static readonly TO = 19; - public static readonly PRIVATE = 20; - public static readonly SHARED = 21; - public static readonly STATE = 22; - public static readonly EDGE = 23; - public static readonly PROJECTION = 24; - public static readonly WITH = 25; - public static readonly USING = 26; - public static readonly VIA = 27; - public static readonly MATERIALIZE = 28; - public static readonly IF = 29; - public static readonly ABSENT = 30; - public static readonly ON = 31; - public static readonly POLICY = 32; - public static readonly DEFAULT = 33; - public static readonly SOURCE = 34; - public static readonly REPOSITORY = 35; - public static readonly COMMIT = 36; - public static readonly REVISION = 37; - public static readonly SEMANTIC_MAJOR = 38; - public static readonly ON_DELETE = 39; - public static readonly RETAIN_OTHER = 40; - public static readonly KEYED = 41; - public static readonly PUBLIC_TRAVERSAL = 42; - public static readonly ID = 43; - public static readonly DOC = 44; - public static readonly MODE = 45; - public static readonly EMITS = 46; - public static readonly RECEIVER = 47; - public static readonly REQUIRES = 48; - public static readonly ANY = 49; - public static readonly GET = 50; - public static readonly SET = 51; - public static readonly WATCH = 52; - public static readonly START = 53; - public static readonly STOP = 54; - public static readonly READ = 55; - public static readonly WRITE = 56; - public static readonly RESOLVE = 57; - public static readonly CONNECT = 58; - public static readonly DISCONNECT = 59; - public static readonly CALL = 60; - public static readonly WATCH_START = 61; - public static readonly WATCH_STOP = 62; - public static readonly SUBSCRIBE = 63; - public static readonly UNSUBSCRIBE = 64; - public static readonly OPTIMISTIC_REGISTER = 65; - public static readonly CRDT = 66; - public static readonly OPTIONAL_ONE = 67; - public static readonly EXACTLY_ONE = 68; - public static readonly MANY_UNIQUE = 69; - public static readonly MANY = 70; - public static readonly ORDERED = 71; - public static readonly UNIT = 72; - public static readonly WATCH_HANDLE = 73; - public static readonly MESSAGE = 74; - public static readonly ATOM_REF = 75; - public static readonly INTERFACE_REF = 76; - public static readonly OPTIONAL = 77; - public static readonly LIST = 78; - public static readonly RECORD = 79; - public static readonly BOOL = 80; - public static readonly BYTES = 81; - public static readonly DOUBLE = 82; - public static readonly INT32 = 83; - public static readonly INT64 = 84; - public static readonly STRING = 85; - public static readonly UINT32 = 86; - public static readonly UINT64 = 87; - public static readonly TRUE = 88; - public static readonly FALSE = 89; - public static readonly NULL = 90; - public static readonly ARROW = 91; - public static readonly COLON = 92; - public static readonly SEMI = 93; - public static readonly COMMA = 94; - public static readonly DOT = 95; - public static readonly LBRACE = 96; - public static readonly RBRACE = 97; - public static readonly LBRACK = 98; - public static readonly RBRACK = 99; - public static readonly LPAREN = 100; - public static readonly RPAREN = 101; - public static readonly LT = 102; - public static readonly GT = 103; - public static readonly INTEGER = 104; - public static readonly JSON_NUMBER = 105; - public static readonly IDENTIFIER = 106; - public static readonly STRING_LITERAL = 107; - public static readonly LINE_COMMENT = 108; - public static readonly BLOCK_COMMENT = 109; - public static readonly WS = 110; + public static readonly TYPE = 2; + public static readonly OBJECT = 3; + public static readonly STORABLE = 4; + public static readonly IMPLEMENTS = 5; + public static readonly REF = 6; + public static readonly FRAGMENT = 7; + public static readonly IMPORT = 8; + public static readonly EXTERNAL = 9; + public static readonly ATOM = 10; + public static readonly INTERFACE = 11; + public static readonly INTERFACES = 12; + public static readonly PACKAGE = 13; + public static readonly VALUE = 14; + public static readonly RELATION = 15; + public static readonly OPERATION = 16; + public static readonly FUNCTION = 17; + public static readonly CONSTRUCTOR = 18; + public static readonly CONSTRUCTS = 19; + public static readonly INPUT = 20; + public static readonly CONFORM = 21; + public static readonly AS = 22; + public static readonly BIND = 23; + public static readonly TO = 24; + public static readonly PRIVATE = 25; + public static readonly SHARED = 26; + public static readonly STATE = 27; + public static readonly EDGE = 28; + public static readonly PROJECTION = 29; + public static readonly WITH = 30; + public static readonly USING = 31; + public static readonly VIA = 32; + public static readonly MATERIALIZE = 33; + public static readonly IF = 34; + public static readonly ABSENT = 35; + public static readonly ON = 36; + public static readonly POLICY = 37; + public static readonly DEFAULT = 38; + public static readonly SOURCE = 39; + public static readonly REPOSITORY = 40; + public static readonly COMMIT = 41; + public static readonly REVISION = 42; + public static readonly SEMANTIC_MAJOR = 43; + public static readonly ON_DELETE = 44; + public static readonly RETAIN_OTHER = 45; + public static readonly KEYED = 46; + public static readonly PUBLIC_TRAVERSAL = 47; + public static readonly ID = 48; + public static readonly DOC = 49; + public static readonly MODE = 50; + public static readonly EMITS = 51; + public static readonly RECEIVER = 52; + public static readonly REQUIRES = 53; + public static readonly ANY = 54; + public static readonly GET = 55; + public static readonly SET = 56; + public static readonly WATCH = 57; + public static readonly START = 58; + public static readonly STOP = 59; + public static readonly READ = 60; + public static readonly WRITE = 61; + public static readonly RESOLVE = 62; + public static readonly CONNECT = 63; + public static readonly DISCONNECT = 64; + public static readonly CALL = 65; + public static readonly WATCH_START = 66; + public static readonly WATCH_STOP = 67; + public static readonly SUBSCRIBE = 68; + public static readonly UNSUBSCRIBE = 69; + public static readonly OPTIMISTIC_REGISTER = 70; + public static readonly CRDT = 71; + public static readonly OPTIONAL_ONE = 72; + public static readonly EXACTLY_ONE = 73; + public static readonly MANY_UNIQUE = 74; + public static readonly MANY = 75; + public static readonly ORDERED = 76; + public static readonly UNIT = 77; + public static readonly WATCH_HANDLE = 78; + public static readonly MESSAGE = 79; + public static readonly ATOM_REF = 80; + public static readonly INTERFACE_REF = 81; + public static readonly OPTIONAL = 82; + public static readonly LIST = 83; + public static readonly RECORD = 84; + public static readonly BOOL = 85; + public static readonly BYTES = 86; + public static readonly DOUBLE = 87; + public static readonly INT32 = 88; + public static readonly INT64 = 89; + public static readonly STRING = 90; + public static readonly UINT32 = 91; + public static readonly UINT64 = 92; + public static readonly TRUE = 93; + public static readonly FALSE = 94; + public static readonly NULL = 95; + public static readonly ARROW = 96; + public static readonly COLON = 97; + public static readonly SEMI = 98; + public static readonly COMMA = 99; + public static readonly DOT = 100; + public static readonly LBRACE = 101; + public static readonly RBRACE = 102; + public static readonly LBRACK = 103; + public static readonly RBRACK = 104; + public static readonly LPAREN = 105; + public static readonly RPAREN = 106; + public static readonly LT = 107; + public static readonly GT = 108; + public static readonly AMP = 109; + public static readonly EQUAL = 110; + public static readonly INTEGER = 111; + public static readonly JSON_NUMBER = 112; + public static readonly IDENTIFIER = 113; + public static readonly STRING_LITERAL = 114; + public static readonly LINE_COMMENT = 115; + public static readonly BLOCK_COMMENT = 116; + public static readonly WS = 117; public static readonly RULE_document = 0; public static readonly RULE_fragmentDecl = 1; public static readonly RULE_sourceImportDecl = 2; @@ -131,110 +138,119 @@ export class QuixosCapabilityParser extends antlr.Parser { public static readonly RULE_resourcePreamble = 8; public static readonly RULE_atomDecl = 9; public static readonly RULE_interfaceResourceDecl = 10; - public static readonly RULE_interfaceMember = 11; - public static readonly RULE_operationMember = 12; - public static readonly RULE_valueMember = 13; - public static readonly RULE_valueMemberOperation = 14; - public static readonly RULE_relationshipMember = 15; - public static readonly RULE_relationshipOperation = 16; - public static readonly RULE_targetConstraint = 17; - public static readonly RULE_packageResourceDecl = 18; - public static readonly RULE_packageExport = 19; - public static readonly RULE_packageOperationExport = 20; - public static readonly RULE_packageFunctionExport = 21; - public static readonly RULE_packageConstructorExport = 22; - public static readonly RULE_eventClause = 23; - public static readonly RULE_operationMode = 24; - public static readonly RULE_receiverRequirement = 25; - public static readonly RULE_identifierList = 26; - public static readonly RULE_dependencyBlock = 27; - public static readonly RULE_dependencyPort = 28; - public static readonly RULE_primitiveList = 29; - public static readonly RULE_primitive = 30; - public static readonly RULE_sharedAttachmentDecl = 31; - public static readonly RULE_attachmentDecl = 32; - public static readonly RULE_stateDecl = 33; - public static readonly RULE_storagePolicy = 34; - public static readonly RULE_edgeDecl = 35; - public static readonly RULE_edgeEndpoint = 36; - public static readonly RULE_conformanceDecl = 37; - public static readonly RULE_conformanceItem = 38; - public static readonly RULE_relationshipMaterializationDecl = 39; - public static readonly RULE_operationBindingDecl = 40; - public static readonly RULE_memberOperationRef = 41; - public static readonly RULE_operationName = 42; - public static readonly RULE_operationProvider = 43; - public static readonly RULE_statePrimitive = 44; - public static readonly RULE_edgePrimitive = 45; - public static readonly RULE_dependencyBindingBlock = 46; - public static readonly RULE_dependencyBinding = 47; - public static readonly RULE_constructorBindingDecl = 48; - public static readonly RULE_valueType = 49; - public static readonly RULE_recordField = 50; - public static readonly RULE_scalarType = 51; - public static readonly RULE_cardinality = 52; - public static readonly RULE_jsonLiteral = 53; - public static readonly RULE_jsonObject = 54; - public static readonly RULE_jsonMember = 55; - public static readonly RULE_jsonArray = 56; - public static readonly RULE_identifier = 57; - public static readonly RULE_stringLiteral = 58; + public static readonly RULE_typeParameters = 11; + public static readonly RULE_typeParameter = 12; + public static readonly RULE_interfaceType = 13; + public static readonly RULE_typeArguments = 14; + public static readonly RULE_typeArgument = 15; + public static readonly RULE_typeAliasDecl = 16; + public static readonly RULE_interfaceMember = 17; + public static readonly RULE_operationMember = 18; + public static readonly RULE_valueMember = 19; + public static readonly RULE_valueMemberOperation = 20; + public static readonly RULE_relationshipMember = 21; + public static readonly RULE_relationshipOperation = 22; + public static readonly RULE_targetConstraint = 23; + public static readonly RULE_packageResourceDecl = 24; + public static readonly RULE_packageExport = 25; + public static readonly RULE_packageOperationExport = 26; + public static readonly RULE_packageFunctionExport = 27; + public static readonly RULE_packageConstructorExport = 28; + public static readonly RULE_eventClause = 29; + public static readonly RULE_operationMode = 30; + public static readonly RULE_receiverRequirement = 31; + public static readonly RULE_identifierList = 32; + public static readonly RULE_dependencyBlock = 33; + public static readonly RULE_dependencyPort = 34; + public static readonly RULE_primitiveList = 35; + public static readonly RULE_primitive = 36; + public static readonly RULE_sharedAttachmentDecl = 37; + public static readonly RULE_attachmentDecl = 38; + public static readonly RULE_stateDecl = 39; + public static readonly RULE_storagePolicy = 40; + public static readonly RULE_edgeDecl = 41; + public static readonly RULE_edgeEndpoint = 42; + public static readonly RULE_conformanceDecl = 43; + public static readonly RULE_conformanceItem = 44; + public static readonly RULE_relationshipMaterializationDecl = 45; + public static readonly RULE_operationBindingDecl = 46; + public static readonly RULE_memberOperationRef = 47; + public static readonly RULE_operationName = 48; + public static readonly RULE_operationProvider = 49; + public static readonly RULE_statePrimitive = 50; + public static readonly RULE_edgePrimitive = 51; + public static readonly RULE_dependencyBindingBlock = 52; + public static readonly RULE_dependencyBinding = 53; + public static readonly RULE_constructorBindingDecl = 54; + public static readonly RULE_valueType = 55; + public static readonly RULE_recordField = 56; + public static readonly RULE_scalarType = 57; + public static readonly RULE_cardinality = 58; + public static readonly RULE_jsonLiteral = 59; + public static readonly RULE_jsonObject = 60; + public static readonly RULE_jsonMember = 61; + public static readonly RULE_jsonArray = 62; + public static readonly RULE_identifier = 63; + public static readonly RULE_stringLiteral = 64; public static readonly literalNames = [ - null, "'workspace'", "'fragment'", "'import'", "'external'", "'atom'", - "'interface'", "'interfaces'", "'package'", "'value'", "'relation'", - "'operation'", "'function'", "'constructor'", "'constructs'", "'input'", - "'conform'", "'as'", "'bind'", "'to'", "'private'", "'shared'", - "'state'", "'edge'", "'projection'", "'with'", "'using'", "'via'", - "'materialize'", "'if'", "'absent'", "'on'", "'policy'", "'default'", - "'source'", "'repository'", "'commit'", "'revision'", "'semantic-major'", - "'on-delete'", "'retain-other'", "'keyed'", "'public-traversal'", - "'id'", "'doc'", "'mode'", "'emits'", "'receiver'", "'requires'", - "'any'", "'get'", "'set'", "'watch'", "'start'", "'stop'", "'read'", - "'write'", "'resolve'", "'connect'", "'disconnect'", "'call'", "'watch-start'", - "'watch-stop'", "'subscribe'", "'unsubscribe'", "'optimistic-register'", - "'crdt'", "'optional-one'", "'exactly-one'", "'many-unique'", "'many'", - "'ordered'", "'unit'", "'watch-handle'", "'message'", "'atom-ref'", - "'interface-ref'", "'optional'", "'list'", "'record'", "'bool'", - "'bytes'", "'double'", "'int32'", "'int64'", "'string'", "'uint32'", - "'uint64'", "'true'", "'false'", "'null'", "'->'", "':'", "';'", - "','", "'.'", "'{'", "'}'", "'['", "']'", "'('", "')'", "'<'", "'>'" + null, "'workspace'", "'type'", "'object'", "'storable'", "'implements'", + "'ref'", "'fragment'", "'import'", "'external'", "'atom'", "'interface'", + "'interfaces'", "'package'", "'value'", "'relation'", "'operation'", + "'function'", "'constructor'", "'constructs'", "'input'", "'conform'", + "'as'", "'bind'", "'to'", "'private'", "'shared'", "'state'", "'edge'", + "'projection'", "'with'", "'using'", "'via'", "'materialize'", "'if'", + "'absent'", "'on'", "'policy'", "'default'", "'source'", "'repository'", + "'commit'", "'revision'", "'semantic-major'", "'on-delete'", "'retain-other'", + "'keyed'", "'public-traversal'", "'id'", "'doc'", "'mode'", "'emits'", + "'receiver'", "'requires'", "'any'", "'get'", "'set'", "'watch'", + "'start'", "'stop'", "'read'", "'write'", "'resolve'", "'connect'", + "'disconnect'", "'call'", "'watch-start'", "'watch-stop'", "'subscribe'", + "'unsubscribe'", "'optimistic-register'", "'crdt'", "'optional-one'", + "'exactly-one'", "'many-unique'", "'many'", "'ordered'", "'unit'", + "'watch-handle'", "'message'", "'atom-ref'", "'interface-ref'", + "'optional'", "'list'", "'record'", "'bool'", "'bytes'", "'double'", + "'int32'", "'int64'", "'string'", "'uint32'", "'uint64'", "'true'", + "'false'", "'null'", "'->'", "':'", "';'", "','", "'.'", "'{'", + "'}'", "'['", "']'", "'('", "')'", "'<'", "'>'", "'&'", "'='" ]; public static readonly symbolicNames = [ - null, "WORKSPACE", "FRAGMENT", "IMPORT", "EXTERNAL", "ATOM", "INTERFACE", - "INTERFACES", "PACKAGE", "VALUE", "RELATION", "OPERATION", "FUNCTION", - "CONSTRUCTOR", "CONSTRUCTS", "INPUT", "CONFORM", "AS", "BIND", "TO", - "PRIVATE", "SHARED", "STATE", "EDGE", "PROJECTION", "WITH", "USING", - "VIA", "MATERIALIZE", "IF", "ABSENT", "ON", "POLICY", "DEFAULT", - "SOURCE", "REPOSITORY", "COMMIT", "REVISION", "SEMANTIC_MAJOR", - "ON_DELETE", "RETAIN_OTHER", "KEYED", "PUBLIC_TRAVERSAL", "ID", - "DOC", "MODE", "EMITS", "RECEIVER", "REQUIRES", "ANY", "GET", "SET", - "WATCH", "START", "STOP", "READ", "WRITE", "RESOLVE", "CONNECT", - "DISCONNECT", "CALL", "WATCH_START", "WATCH_STOP", "SUBSCRIBE", - "UNSUBSCRIBE", "OPTIMISTIC_REGISTER", "CRDT", "OPTIONAL_ONE", "EXACTLY_ONE", - "MANY_UNIQUE", "MANY", "ORDERED", "UNIT", "WATCH_HANDLE", "MESSAGE", - "ATOM_REF", "INTERFACE_REF", "OPTIONAL", "LIST", "RECORD", "BOOL", - "BYTES", "DOUBLE", "INT32", "INT64", "STRING", "UINT32", "UINT64", - "TRUE", "FALSE", "NULL", "ARROW", "COLON", "SEMI", "COMMA", "DOT", - "LBRACE", "RBRACE", "LBRACK", "RBRACK", "LPAREN", "RPAREN", "LT", - "GT", "INTEGER", "JSON_NUMBER", "IDENTIFIER", "STRING_LITERAL", - "LINE_COMMENT", "BLOCK_COMMENT", "WS" + null, "WORKSPACE", "TYPE", "OBJECT", "STORABLE", "IMPLEMENTS", "REF", + "FRAGMENT", "IMPORT", "EXTERNAL", "ATOM", "INTERFACE", "INTERFACES", + "PACKAGE", "VALUE", "RELATION", "OPERATION", "FUNCTION", "CONSTRUCTOR", + "CONSTRUCTS", "INPUT", "CONFORM", "AS", "BIND", "TO", "PRIVATE", + "SHARED", "STATE", "EDGE", "PROJECTION", "WITH", "USING", "VIA", + "MATERIALIZE", "IF", "ABSENT", "ON", "POLICY", "DEFAULT", "SOURCE", + "REPOSITORY", "COMMIT", "REVISION", "SEMANTIC_MAJOR", "ON_DELETE", + "RETAIN_OTHER", "KEYED", "PUBLIC_TRAVERSAL", "ID", "DOC", "MODE", + "EMITS", "RECEIVER", "REQUIRES", "ANY", "GET", "SET", "WATCH", "START", + "STOP", "READ", "WRITE", "RESOLVE", "CONNECT", "DISCONNECT", "CALL", + "WATCH_START", "WATCH_STOP", "SUBSCRIBE", "UNSUBSCRIBE", "OPTIMISTIC_REGISTER", + "CRDT", "OPTIONAL_ONE", "EXACTLY_ONE", "MANY_UNIQUE", "MANY", "ORDERED", + "UNIT", "WATCH_HANDLE", "MESSAGE", "ATOM_REF", "INTERFACE_REF", + "OPTIONAL", "LIST", "RECORD", "BOOL", "BYTES", "DOUBLE", "INT32", + "INT64", "STRING", "UINT32", "UINT64", "TRUE", "FALSE", "NULL", + "ARROW", "COLON", "SEMI", "COMMA", "DOT", "LBRACE", "RBRACE", "LBRACK", + "RBRACK", "LPAREN", "RPAREN", "LT", "GT", "AMP", "EQUAL", "INTEGER", + "JSON_NUMBER", "IDENTIFIER", "STRING_LITERAL", "LINE_COMMENT", "BLOCK_COMMENT", + "WS" ]; public static readonly ruleNames = [ "document", "fragmentDecl", "sourceImportDecl", "workspaceDecl", "workspaceItem", "resourceImportDecl", "externalAtomDecl", "externalInterfaceDecl", - "resourcePreamble", "atomDecl", "interfaceResourceDecl", "interfaceMember", - "operationMember", "valueMember", "valueMemberOperation", "relationshipMember", - "relationshipOperation", "targetConstraint", "packageResourceDecl", - "packageExport", "packageOperationExport", "packageFunctionExport", - "packageConstructorExport", "eventClause", "operationMode", "receiverRequirement", - "identifierList", "dependencyBlock", "dependencyPort", "primitiveList", - "primitive", "sharedAttachmentDecl", "attachmentDecl", "stateDecl", - "storagePolicy", "edgeDecl", "edgeEndpoint", "conformanceDecl", - "conformanceItem", "relationshipMaterializationDecl", "operationBindingDecl", - "memberOperationRef", "operationName", "operationProvider", "statePrimitive", - "edgePrimitive", "dependencyBindingBlock", "dependencyBinding", + "resourcePreamble", "atomDecl", "interfaceResourceDecl", "typeParameters", + "typeParameter", "interfaceType", "typeArguments", "typeArgument", + "typeAliasDecl", "interfaceMember", "operationMember", "valueMember", + "valueMemberOperation", "relationshipMember", "relationshipOperation", + "targetConstraint", "packageResourceDecl", "packageExport", "packageOperationExport", + "packageFunctionExport", "packageConstructorExport", "eventClause", + "operationMode", "receiverRequirement", "identifierList", "dependencyBlock", + "dependencyPort", "primitiveList", "primitive", "sharedAttachmentDecl", + "attachmentDecl", "stateDecl", "storagePolicy", "edgeDecl", "edgeEndpoint", + "conformanceDecl", "conformanceItem", "relationshipMaterializationDecl", + "operationBindingDecl", "memberOperationRef", "operationName", "operationProvider", + "statePrimitive", "edgePrimitive", "dependencyBindingBlock", "dependencyBinding", "constructorBindingDecl", "valueType", "recordField", "scalarType", "cardinality", "jsonLiteral", "jsonObject", "jsonMember", "jsonArray", "identifier", "stringLiteral", @@ -258,42 +274,42 @@ export class QuixosCapabilityParser extends antlr.Parser { let localContext = new DocumentContext(this.context, this.state); this.enterRule(localContext, 0, QuixosCapabilityParser.RULE_document); try { - this.state = 130; + this.state = 142; this.errorHandler.sync(this); switch (this.interpreter.adaptivePredict(this.tokenStream, 0, this.context) ) { case 1: this.enterOuterAlt(localContext, 1); { - this.state = 118; + this.state = 130; this.workspaceDecl(); - this.state = 119; + this.state = 131; this.match(QuixosCapabilityParser.EOF); } break; case 2: this.enterOuterAlt(localContext, 2); { - this.state = 121; + this.state = 133; this.interfaceResourceDecl(); - this.state = 122; + this.state = 134; this.match(QuixosCapabilityParser.EOF); } break; case 3: this.enterOuterAlt(localContext, 3); { - this.state = 124; + this.state = 136; this.packageResourceDecl(); - this.state = 125; + this.state = 137; this.match(QuixosCapabilityParser.EOF); } break; case 4: this.enterOuterAlt(localContext, 4); { - this.state = 127; + this.state = 139; this.fragmentDecl(); - this.state = 128; + this.state = 140; this.match(QuixosCapabilityParser.EOF); } break; @@ -319,25 +335,25 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 132; + this.state = 144; this.match(QuixosCapabilityParser.FRAGMENT); - this.state = 133; + this.state = 145; this.match(QuixosCapabilityParser.LBRACE); - this.state = 137; + this.state = 149; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 2170920) !== 0)) { + while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 69469444) !== 0)) { { { - this.state = 134; + this.state = 146; this.workspaceItem(); } } - this.state = 139; + this.state = 151; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 140; + this.state = 152; this.match(QuixosCapabilityParser.RBRACE); } } @@ -360,11 +376,11 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 142; + this.state = 154; this.match(QuixosCapabilityParser.IMPORT); - this.state = 143; + this.state = 155; this.stringLiteral(); - this.state = 144; + this.state = 156; this.match(QuixosCapabilityParser.SEMI); } } @@ -388,39 +404,39 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 146; - this.match(QuixosCapabilityParser.WORKSPACE); - this.state = 147; - this.identifier(); - this.state = 148; - this.match(QuixosCapabilityParser.ID); - this.state = 149; - this.stringLiteral(); - this.state = 150; - this.match(QuixosCapabilityParser.REVISION); - this.state = 151; - this.stringLiteral(); - this.state = 152; - this.match(QuixosCapabilityParser.COMMIT); - this.state = 153; - this.stringLiteral(); - this.state = 154; - this.match(QuixosCapabilityParser.LBRACE); this.state = 158; + this.match(QuixosCapabilityParser.WORKSPACE); + this.state = 159; + this.identifier(); + this.state = 160; + this.match(QuixosCapabilityParser.ID); + this.state = 161; + this.stringLiteral(); + this.state = 162; + this.match(QuixosCapabilityParser.REVISION); + this.state = 163; + this.stringLiteral(); + this.state = 164; + this.match(QuixosCapabilityParser.COMMIT); + this.state = 165; + this.stringLiteral(); + this.state = 166; + this.match(QuixosCapabilityParser.LBRACE); + this.state = 170; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 2170920) !== 0)) { + while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 69469444) !== 0)) { { { - this.state = 155; + this.state = 167; this.workspaceItem(); } } - this.state = 160; + this.state = 172; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 161; + this.state = 173; this.match(QuixosCapabilityParser.RBRACE); } } @@ -441,51 +457,58 @@ export class QuixosCapabilityParser extends antlr.Parser { let localContext = new WorkspaceItemContext(this.context, this.state); this.enterRule(localContext, 8, QuixosCapabilityParser.RULE_workspaceItem); try { - this.state = 169; + this.state = 182; this.errorHandler.sync(this); switch (this.interpreter.adaptivePredict(this.tokenStream, 3, this.context) ) { case 1: this.enterOuterAlt(localContext, 1); { - this.state = 163; + this.state = 175; this.sourceImportDecl(); } break; case 2: this.enterOuterAlt(localContext, 2); { - this.state = 164; + this.state = 176; this.atomDecl(); } break; case 3: this.enterOuterAlt(localContext, 3); { - this.state = 165; + this.state = 177; this.resourceImportDecl(); } break; case 4: this.enterOuterAlt(localContext, 4); { - this.state = 166; + this.state = 178; this.sharedAttachmentDecl(); } break; case 5: this.enterOuterAlt(localContext, 5); { - this.state = 167; + this.state = 179; this.conformanceDecl(); } break; case 6: this.enterOuterAlt(localContext, 6); { - this.state = 168; + this.state = 180; this.constructorBindingDecl(); } break; + case 7: + this.enterOuterAlt(localContext, 7); + { + this.state = 181; + this.typeAliasDecl(); + } + break; } } catch (re) { @@ -505,32 +528,32 @@ export class QuixosCapabilityParser extends antlr.Parser { let localContext = new ResourceImportDeclContext(this.context, this.state); this.enterRule(localContext, 10, QuixosCapabilityParser.RULE_resourceImportDecl); try { - this.state = 181; + this.state = 194; this.errorHandler.sync(this); switch (this.interpreter.adaptivePredict(this.tokenStream, 4, this.context) ) { case 1: this.enterOuterAlt(localContext, 1); { - this.state = 171; + this.state = 184; this.match(QuixosCapabilityParser.IMPORT); - this.state = 172; + this.state = 185; this.match(QuixosCapabilityParser.INTERFACE); - this.state = 173; + this.state = 186; this.identifier(); - this.state = 174; + this.state = 187; this.match(QuixosCapabilityParser.SEMI); } break; case 2: this.enterOuterAlt(localContext, 2); { - this.state = 176; + this.state = 189; this.match(QuixosCapabilityParser.IMPORT); - this.state = 177; + this.state = 190; this.match(QuixosCapabilityParser.PACKAGE); - this.state = 178; + this.state = 191; this.identifier(); - this.state = 179; + this.state = 192; this.match(QuixosCapabilityParser.SEMI); } break; @@ -555,17 +578,17 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 183; + this.state = 196; this.match(QuixosCapabilityParser.EXTERNAL); - this.state = 184; + this.state = 197; this.match(QuixosCapabilityParser.ATOM); - this.state = 185; + this.state = 198; this.identifier(); - this.state = 186; + this.state = 199; this.match(QuixosCapabilityParser.ID); - this.state = 187; + this.state = 200; this.stringLiteral(); - this.state = 188; + this.state = 201; this.match(QuixosCapabilityParser.SEMI); } } @@ -588,17 +611,17 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 190; + this.state = 203; this.match(QuixosCapabilityParser.EXTERNAL); - this.state = 191; + this.state = 204; this.match(QuixosCapabilityParser.INTERFACE); - this.state = 192; + this.state = 205; this.identifier(); - this.state = 193; + this.state = 206; this.match(QuixosCapabilityParser.REVISION); - this.state = 194; + this.state = 207; this.stringLiteral(); - this.state = 195; + this.state = 208; this.match(QuixosCapabilityParser.SEMI); } } @@ -619,30 +642,37 @@ export class QuixosCapabilityParser extends antlr.Parser { let localContext = new ResourcePreambleContext(this.context, this.state); this.enterRule(localContext, 16, QuixosCapabilityParser.RULE_resourcePreamble); try { - this.state = 200; + this.state = 214; this.errorHandler.sync(this); switch (this.interpreter.adaptivePredict(this.tokenStream, 5, this.context) ) { case 1: this.enterOuterAlt(localContext, 1); { - this.state = 197; + this.state = 210; this.resourceImportDecl(); } break; case 2: this.enterOuterAlt(localContext, 2); { - this.state = 198; + this.state = 211; this.externalAtomDecl(); } break; case 3: this.enterOuterAlt(localContext, 3); { - this.state = 199; + this.state = 212; this.externalInterfaceDecl(); } break; + case 4: + this.enterOuterAlt(localContext, 4); + { + this.state = 213; + this.typeAliasDecl(); + } + break; } } catch (re) { @@ -665,27 +695,27 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 202; + this.state = 216; this.match(QuixosCapabilityParser.ATOM); - this.state = 203; + this.state = 217; this.identifier(); - this.state = 204; + this.state = 218; this.match(QuixosCapabilityParser.ID); - this.state = 205; + this.state = 219; this.stringLiteral(); - this.state = 208; + this.state = 222; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 44) { + if (_la === 49) { { - this.state = 206; + this.state = 220; this.match(QuixosCapabilityParser.DOC); - this.state = 207; + this.state = 221; this.stringLiteral(); } } - this.state = 210; + this.state = 224; this.match(QuixosCapabilityParser.SEMI); } } @@ -709,49 +739,87 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 215; + this.state = 229; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (_la === 3 || _la === 4) { + while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 772) !== 0)) { { { - this.state = 212; + this.state = 226; this.resourcePreamble(); } } - this.state = 217; + this.state = 231; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 218; + this.state = 232; this.match(QuixosCapabilityParser.INTERFACE); - this.state = 219; + this.state = 233; this.identifier(); - this.state = 220; - this.match(QuixosCapabilityParser.ID); - this.state = 221; - this.stringLiteral(); - this.state = 222; - this.match(QuixosCapabilityParser.REVISION); - this.state = 223; - this.stringLiteral(); - this.state = 224; - this.match(QuixosCapabilityParser.LBRACE); - this.state = 228; + this.state = 235; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 3584) !== 0)) { + if (_la === 107) { + { + this.state = 234; + this.typeParameters(); + } + } + + this.state = 237; + this.match(QuixosCapabilityParser.ID); + this.state = 238; + this.stringLiteral(); + this.state = 239; + this.match(QuixosCapabilityParser.REVISION); + this.state = 240; + this.stringLiteral(); + this.state = 250; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + if (_la === 53) { + { + this.state = 241; + this.match(QuixosCapabilityParser.REQUIRES); + this.state = 242; + this.interfaceType(); + this.state = 247; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + while (_la === 99) { + { + { + this.state = 243; + this.match(QuixosCapabilityParser.COMMA); + this.state = 244; + this.interfaceType(); + } + } + this.state = 249; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + } + } + } + + this.state = 252; + this.match(QuixosCapabilityParser.LBRACE); + this.state = 256; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 114688) !== 0)) { { { - this.state = 225; + this.state = 253; this.interfaceMember(); } } - this.state = 230; + this.state = 258; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 231; + this.state = 259; this.match(QuixosCapabilityParser.RBRACE); } } @@ -768,31 +836,354 @@ export class QuixosCapabilityParser extends antlr.Parser { } return localContext; } + public typeParameters(): TypeParametersContext { + let localContext = new TypeParametersContext(this.context, this.state); + this.enterRule(localContext, 22, QuixosCapabilityParser.RULE_typeParameters); + let _la: number; + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 261; + this.match(QuixosCapabilityParser.LT); + this.state = 262; + this.typeParameter(); + this.state = 267; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + while (_la === 99) { + { + { + this.state = 263; + this.match(QuixosCapabilityParser.COMMA); + this.state = 264; + this.typeParameter(); + } + } + this.state = 269; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + } + this.state = 270; + this.match(QuixosCapabilityParser.GT); + } + } + catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return localContext; + } + public typeParameter(): TypeParameterContext { + let localContext = new TypeParameterContext(this.context, this.state); + this.enterRule(localContext, 24, QuixosCapabilityParser.RULE_typeParameter); + let _la: number; + try { + this.state = 291; + this.errorHandler.sync(this); + switch (this.tokenStream.LA(1)) { + case QuixosCapabilityParser.VALUE: + this.enterOuterAlt(localContext, 1); + { + this.state = 272; + this.match(QuixosCapabilityParser.VALUE); + this.state = 273; + this.identifier(); + this.state = 276; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + if (_la === 97) { + { + this.state = 274; + this.match(QuixosCapabilityParser.COLON); + this.state = 275; + this.match(QuixosCapabilityParser.STORABLE); + } + } + + } + break; + case QuixosCapabilityParser.OBJECT: + this.enterOuterAlt(localContext, 2); + { + this.state = 278; + this.match(QuixosCapabilityParser.OBJECT); + this.state = 279; + this.identifier(); + this.state = 289; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + if (_la === 5) { + { + this.state = 280; + this.match(QuixosCapabilityParser.IMPLEMENTS); + this.state = 281; + this.interfaceType(); + this.state = 286; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + while (_la === 109) { + { + { + this.state = 282; + this.match(QuixosCapabilityParser.AMP); + this.state = 283; + this.interfaceType(); + } + } + this.state = 288; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + } + } + } + + } + break; + default: + throw new antlr.NoViableAltException(this); + } + } + catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return localContext; + } + public interfaceType(): InterfaceTypeContext { + let localContext = new InterfaceTypeContext(this.context, this.state); + this.enterRule(localContext, 26, QuixosCapabilityParser.RULE_interfaceType); + let _la: number; + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 293; + this.identifier(); + this.state = 295; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + if (_la === 107) { + { + this.state = 294; + this.typeArguments(); + } + } + + } + } + catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return localContext; + } + public typeArguments(): TypeArgumentsContext { + let localContext = new TypeArgumentsContext(this.context, this.state); + this.enterRule(localContext, 28, QuixosCapabilityParser.RULE_typeArguments); + let _la: number; + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 297; + this.match(QuixosCapabilityParser.LT); + this.state = 298; + this.typeArgument(); + this.state = 303; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + while (_la === 99) { + { + { + this.state = 299; + this.match(QuixosCapabilityParser.COMMA); + this.state = 300; + this.typeArgument(); + } + } + this.state = 305; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + } + this.state = 306; + this.match(QuixosCapabilityParser.GT); + } + } + catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return localContext; + } + public typeArgument(): TypeArgumentContext { + let localContext = new TypeArgumentContext(this.context, this.state); + this.enterRule(localContext, 30, QuixosCapabilityParser.RULE_typeArgument); + try { + this.state = 315; + this.errorHandler.sync(this); + switch (this.tokenStream.LA(1)) { + case QuixosCapabilityParser.ATOM: + this.enterOuterAlt(localContext, 1); + { + this.state = 308; + this.match(QuixosCapabilityParser.ATOM); + this.state = 309; + this.identifier(); + } + break; + case QuixosCapabilityParser.INTERFACE: + this.enterOuterAlt(localContext, 2); + { + this.state = 310; + this.match(QuixosCapabilityParser.INTERFACE); + this.state = 311; + this.interfaceType(); + } + break; + case QuixosCapabilityParser.OBJECT: + this.enterOuterAlt(localContext, 3); + { + this.state = 312; + this.match(QuixosCapabilityParser.OBJECT); + this.state = 313; + this.identifier(); + } + break; + case QuixosCapabilityParser.REF: + case QuixosCapabilityParser.SOURCE: + case QuixosCapabilityParser.UNIT: + case QuixosCapabilityParser.WATCH_HANDLE: + case QuixosCapabilityParser.MESSAGE: + case QuixosCapabilityParser.ATOM_REF: + case QuixosCapabilityParser.INTERFACE_REF: + case QuixosCapabilityParser.OPTIONAL: + case QuixosCapabilityParser.LIST: + case QuixosCapabilityParser.RECORD: + case QuixosCapabilityParser.BOOL: + case QuixosCapabilityParser.BYTES: + case QuixosCapabilityParser.DOUBLE: + case QuixosCapabilityParser.INT32: + case QuixosCapabilityParser.INT64: + case QuixosCapabilityParser.STRING: + case QuixosCapabilityParser.UINT32: + case QuixosCapabilityParser.UINT64: + case QuixosCapabilityParser.IDENTIFIER: + this.enterOuterAlt(localContext, 4); + { + this.state = 314; + this.valueType(); + } + break; + default: + throw new antlr.NoViableAltException(this); + } + } + catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return localContext; + } + public typeAliasDecl(): TypeAliasDeclContext { + let localContext = new TypeAliasDeclContext(this.context, this.state); + this.enterRule(localContext, 32, QuixosCapabilityParser.RULE_typeAliasDecl); + let _la: number; + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 317; + this.match(QuixosCapabilityParser.TYPE); + this.state = 318; + this.identifier(); + this.state = 320; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + if (_la === 107) { + { + this.state = 319; + this.typeParameters(); + } + } + + this.state = 322; + this.match(QuixosCapabilityParser.EQUAL); + this.state = 323; + this.valueType(); + this.state = 324; + this.match(QuixosCapabilityParser.SEMI); + } + } + catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return localContext; + } public interfaceMember(): InterfaceMemberContext { let localContext = new InterfaceMemberContext(this.context, this.state); - this.enterRule(localContext, 22, QuixosCapabilityParser.RULE_interfaceMember); + this.enterRule(localContext, 34, QuixosCapabilityParser.RULE_interfaceMember); try { - this.state = 236; + this.state = 329; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.VALUE: this.enterOuterAlt(localContext, 1); { - this.state = 233; + this.state = 326; this.valueMember(); } break; case QuixosCapabilityParser.RELATION: this.enterOuterAlt(localContext, 2); { - this.state = 234; + this.state = 327; this.relationshipMember(); } break; case QuixosCapabilityParser.OPERATION: this.enterOuterAlt(localContext, 3); { - this.state = 235; + this.state = 328; this.operationMember(); } break; @@ -815,37 +1206,37 @@ export class QuixosCapabilityParser extends antlr.Parser { } public operationMember(): OperationMemberContext { let localContext = new OperationMemberContext(this.context, this.state); - this.enterRule(localContext, 24, QuixosCapabilityParser.RULE_operationMember); + this.enterRule(localContext, 36, QuixosCapabilityParser.RULE_operationMember); try { this.enterOuterAlt(localContext, 1); { - this.state = 238; + this.state = 331; this.match(QuixosCapabilityParser.OPERATION); - this.state = 239; + this.state = 332; this.identifier(); - this.state = 240; + this.state = 333; this.match(QuixosCapabilityParser.ID); - this.state = 241; + this.state = 334; this.stringLiteral(); - this.state = 242; + this.state = 335; this.match(QuixosCapabilityParser.COLON); - this.state = 243; + this.state = 336; this.valueType(); - this.state = 244; + this.state = 337; this.match(QuixosCapabilityParser.ARROW); - this.state = 245; + this.state = 338; this.valueType(); - this.state = 246; + this.state = 339; this.match(QuixosCapabilityParser.LBRACE); - this.state = 247; + this.state = 340; this.match(QuixosCapabilityParser.CALL); - this.state = 248; + this.state = 341; this.match(QuixosCapabilityParser.ID); - this.state = 249; + this.state = 342; this.stringLiteral(); - this.state = 250; + this.state = 343; this.match(QuixosCapabilityParser.SEMI); - this.state = 251; + this.state = 344; this.match(QuixosCapabilityParser.RBRACE); } } @@ -864,40 +1255,40 @@ export class QuixosCapabilityParser extends antlr.Parser { } public valueMember(): ValueMemberContext { let localContext = new ValueMemberContext(this.context, this.state); - this.enterRule(localContext, 26, QuixosCapabilityParser.RULE_valueMember); + this.enterRule(localContext, 38, QuixosCapabilityParser.RULE_valueMember); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 253; + this.state = 346; this.match(QuixosCapabilityParser.VALUE); - this.state = 254; + this.state = 347; this.identifier(); - this.state = 255; + this.state = 348; this.match(QuixosCapabilityParser.ID); - this.state = 256; + this.state = 349; this.stringLiteral(); - this.state = 257; + this.state = 350; this.match(QuixosCapabilityParser.COLON); - this.state = 258; + this.state = 351; this.valueType(); - this.state = 259; + this.state = 352; this.match(QuixosCapabilityParser.LBRACE); - this.state = 263; + this.state = 356; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (((((_la - 50)) & ~0x1F) === 0 && ((1 << (_la - 50)) & 7) !== 0)) { + while (((((_la - 55)) & ~0x1F) === 0 && ((1 << (_la - 55)) & 7) !== 0)) { { { - this.state = 260; + this.state = 353; this.valueMemberOperation(); } } - this.state = 265; + this.state = 358; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 266; + this.state = 359; this.match(QuixosCapabilityParser.RBRACE); } } @@ -916,55 +1307,55 @@ export class QuixosCapabilityParser extends antlr.Parser { } public valueMemberOperation(): ValueMemberOperationContext { let localContext = new ValueMemberOperationContext(this.context, this.state); - this.enterRule(localContext, 28, QuixosCapabilityParser.RULE_valueMemberOperation); + this.enterRule(localContext, 40, QuixosCapabilityParser.RULE_valueMemberOperation); try { - this.state = 287; + this.state = 380; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.GET: this.enterOuterAlt(localContext, 1); { - this.state = 268; + this.state = 361; this.match(QuixosCapabilityParser.GET); - this.state = 269; + this.state = 362; this.match(QuixosCapabilityParser.ID); - this.state = 270; + this.state = 363; this.stringLiteral(); - this.state = 271; + this.state = 364; this.match(QuixosCapabilityParser.SEMI); } break; case QuixosCapabilityParser.SET: this.enterOuterAlt(localContext, 2); { - this.state = 273; + this.state = 366; this.match(QuixosCapabilityParser.SET); - this.state = 274; + this.state = 367; this.match(QuixosCapabilityParser.ID); - this.state = 275; + this.state = 368; this.stringLiteral(); - this.state = 276; + this.state = 369; this.match(QuixosCapabilityParser.SEMI); } break; case QuixosCapabilityParser.WATCH: this.enterOuterAlt(localContext, 3); { - this.state = 278; + this.state = 371; this.match(QuixosCapabilityParser.WATCH); - this.state = 279; + this.state = 372; this.match(QuixosCapabilityParser.START); - this.state = 280; + this.state = 373; this.match(QuixosCapabilityParser.ID); - this.state = 281; + this.state = 374; this.stringLiteral(); - this.state = 282; + this.state = 375; this.match(QuixosCapabilityParser.STOP); - this.state = 283; + this.state = 376; this.match(QuixosCapabilityParser.ID); - this.state = 284; + this.state = 377; this.stringLiteral(); - this.state = 285; + this.state = 378; this.match(QuixosCapabilityParser.SEMI); } break; @@ -987,52 +1378,52 @@ export class QuixosCapabilityParser extends antlr.Parser { } public relationshipMember(): RelationshipMemberContext { let localContext = new RelationshipMemberContext(this.context, this.state); - this.enterRule(localContext, 30, QuixosCapabilityParser.RULE_relationshipMember); + this.enterRule(localContext, 42, QuixosCapabilityParser.RULE_relationshipMember); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 289; + this.state = 382; this.match(QuixosCapabilityParser.RELATION); - this.state = 290; + this.state = 383; this.identifier(); - this.state = 291; + this.state = 384; this.match(QuixosCapabilityParser.ID); - this.state = 292; + this.state = 385; this.stringLiteral(); - this.state = 293; + this.state = 386; this.match(QuixosCapabilityParser.COLON); - this.state = 294; + this.state = 387; this.cardinality(); - this.state = 295; + this.state = 388; this.targetConstraint(); - this.state = 297; + this.state = 390; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 71) { + if (_la === 76) { { - this.state = 296; + this.state = 389; this.match(QuixosCapabilityParser.ORDERED); } } - this.state = 299; + this.state = 392; this.match(QuixosCapabilityParser.LBRACE); - this.state = 303; + this.state = 396; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (((((_la - 52)) & ~0x1F) === 0 && ((1 << (_la - 52)) & 225) !== 0)) { + while (((((_la - 57)) & ~0x1F) === 0 && ((1 << (_la - 57)) & 225) !== 0)) { { { - this.state = 300; + this.state = 393; this.relationshipOperation(); } } - this.state = 305; + this.state = 398; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 306; + this.state = 399; this.match(QuixosCapabilityParser.RBRACE); } } @@ -1051,68 +1442,68 @@ export class QuixosCapabilityParser extends antlr.Parser { } public relationshipOperation(): RelationshipOperationContext { let localContext = new RelationshipOperationContext(this.context, this.state); - this.enterRule(localContext, 32, QuixosCapabilityParser.RULE_relationshipOperation); + this.enterRule(localContext, 44, QuixosCapabilityParser.RULE_relationshipOperation); try { - this.state = 332; + this.state = 425; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.RESOLVE: this.enterOuterAlt(localContext, 1); { - this.state = 308; + this.state = 401; this.match(QuixosCapabilityParser.RESOLVE); - this.state = 309; + this.state = 402; this.match(QuixosCapabilityParser.ID); - this.state = 310; + this.state = 403; this.stringLiteral(); - this.state = 311; + this.state = 404; this.match(QuixosCapabilityParser.SEMI); } break; case QuixosCapabilityParser.CONNECT: this.enterOuterAlt(localContext, 2); { - this.state = 313; + this.state = 406; this.match(QuixosCapabilityParser.CONNECT); - this.state = 314; + this.state = 407; this.match(QuixosCapabilityParser.ID); - this.state = 315; + this.state = 408; this.stringLiteral(); - this.state = 316; + this.state = 409; this.match(QuixosCapabilityParser.SEMI); } break; case QuixosCapabilityParser.DISCONNECT: this.enterOuterAlt(localContext, 3); { - this.state = 318; + this.state = 411; this.match(QuixosCapabilityParser.DISCONNECT); - this.state = 319; + this.state = 412; this.match(QuixosCapabilityParser.ID); - this.state = 320; + this.state = 413; this.stringLiteral(); - this.state = 321; + this.state = 414; this.match(QuixosCapabilityParser.SEMI); } break; case QuixosCapabilityParser.WATCH: this.enterOuterAlt(localContext, 4); { - this.state = 323; + this.state = 416; this.match(QuixosCapabilityParser.WATCH); - this.state = 324; + this.state = 417; this.match(QuixosCapabilityParser.START); - this.state = 325; + this.state = 418; this.match(QuixosCapabilityParser.ID); - this.state = 326; + this.state = 419; this.stringLiteral(); - this.state = 327; + this.state = 420; this.match(QuixosCapabilityParser.STOP); - this.state = 328; + this.state = 421; this.match(QuixosCapabilityParser.ID); - this.state = 329; + this.state = 422; this.stringLiteral(); - this.state = 330; + this.state = 423; this.match(QuixosCapabilityParser.SEMI); } break; @@ -1135,26 +1526,46 @@ export class QuixosCapabilityParser extends antlr.Parser { } public targetConstraint(): TargetConstraintContext { let localContext = new TargetConstraintContext(this.context, this.state); - this.enterRule(localContext, 34, QuixosCapabilityParser.RULE_targetConstraint); + this.enterRule(localContext, 46, QuixosCapabilityParser.RULE_targetConstraint); + let _la: number; try { - this.state = 338; + this.state = 436; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.ATOM: this.enterOuterAlt(localContext, 1); { - this.state = 334; + this.state = 427; this.match(QuixosCapabilityParser.ATOM); - this.state = 335; + this.state = 428; this.identifier(); } break; case QuixosCapabilityParser.INTERFACE: this.enterOuterAlt(localContext, 2); { - this.state = 336; + this.state = 429; this.match(QuixosCapabilityParser.INTERFACE); - this.state = 337; + this.state = 430; + this.identifier(); + this.state = 432; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + if (_la === 107) { + { + this.state = 431; + this.typeArguments(); + } + } + + } + break; + case QuixosCapabilityParser.OBJECT: + this.enterOuterAlt(localContext, 3); + { + this.state = 434; + this.match(QuixosCapabilityParser.OBJECT); + this.state = 435; this.identifier(); } break; @@ -1177,66 +1588,66 @@ export class QuixosCapabilityParser extends antlr.Parser { } public packageResourceDecl(): PackageResourceDeclContext { let localContext = new PackageResourceDeclContext(this.context, this.state); - this.enterRule(localContext, 36, QuixosCapabilityParser.RULE_packageResourceDecl); + this.enterRule(localContext, 48, QuixosCapabilityParser.RULE_packageResourceDecl); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 343; + this.state = 441; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (_la === 3 || _la === 4) { + while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 772) !== 0)) { { { - this.state = 340; + this.state = 438; this.resourcePreamble(); } } - this.state = 345; + this.state = 443; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 346; + this.state = 444; this.match(QuixosCapabilityParser.PACKAGE); - this.state = 347; + this.state = 445; this.identifier(); - this.state = 348; + this.state = 446; this.match(QuixosCapabilityParser.ID); - this.state = 349; + this.state = 447; this.stringLiteral(); - this.state = 350; + this.state = 448; this.match(QuixosCapabilityParser.REVISION); - this.state = 351; + this.state = 449; this.stringLiteral(); - this.state = 354; + this.state = 452; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 38) { + if (_la === 43) { { - this.state = 352; + this.state = 450; this.match(QuixosCapabilityParser.SEMANTIC_MAJOR); - this.state = 353; + this.state = 451; this.match(QuixosCapabilityParser.INTEGER); } } - this.state = 356; + this.state = 454; this.match(QuixosCapabilityParser.LBRACE); - this.state = 360; + this.state = 458; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 14336) !== 0)) { + while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 458752) !== 0)) { { { - this.state = 357; + this.state = 455; this.packageExport(); } } - this.state = 362; + this.state = 460; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 363; + this.state = 461; this.match(QuixosCapabilityParser.RBRACE); } } @@ -1255,29 +1666,29 @@ export class QuixosCapabilityParser extends antlr.Parser { } public packageExport(): PackageExportContext { let localContext = new PackageExportContext(this.context, this.state); - this.enterRule(localContext, 38, QuixosCapabilityParser.RULE_packageExport); + this.enterRule(localContext, 50, QuixosCapabilityParser.RULE_packageExport); try { - this.state = 368; + this.state = 466; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.OPERATION: this.enterOuterAlt(localContext, 1); { - this.state = 365; + this.state = 463; this.packageOperationExport(); } break; case QuixosCapabilityParser.FUNCTION: this.enterOuterAlt(localContext, 2); { - this.state = 366; + this.state = 464; this.packageFunctionExport(); } break; case QuixosCapabilityParser.CONSTRUCTOR: this.enterOuterAlt(localContext, 3); { - this.state = 367; + this.state = 465; this.packageConstructorExport(); } break; @@ -1300,56 +1711,66 @@ export class QuixosCapabilityParser extends antlr.Parser { } public packageOperationExport(): PackageOperationExportContext { let localContext = new PackageOperationExportContext(this.context, this.state); - this.enterRule(localContext, 40, QuixosCapabilityParser.RULE_packageOperationExport); + this.enterRule(localContext, 52, QuixosCapabilityParser.RULE_packageOperationExport); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 370; + this.state = 468; this.match(QuixosCapabilityParser.OPERATION); - this.state = 371; + this.state = 469; this.identifier(); - this.state = 372; - this.match(QuixosCapabilityParser.ID); - this.state = 373; - this.stringLiteral(); - this.state = 374; - this.match(QuixosCapabilityParser.COLON); - this.state = 375; - this.valueType(); - this.state = 376; - this.match(QuixosCapabilityParser.ARROW); - this.state = 377; - this.valueType(); - this.state = 378; - this.match(QuixosCapabilityParser.MODE); - this.state = 379; - this.operationMode(); - this.state = 381; + this.state = 471; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 46) { + if (_la === 107) { { - this.state = 380; + this.state = 470; + this.typeParameters(); + } + } + + this.state = 473; + this.match(QuixosCapabilityParser.ID); + this.state = 474; + this.stringLiteral(); + this.state = 475; + this.match(QuixosCapabilityParser.COLON); + this.state = 476; + this.valueType(); + this.state = 477; + this.match(QuixosCapabilityParser.ARROW); + this.state = 478; + this.valueType(); + this.state = 479; + this.match(QuixosCapabilityParser.MODE); + this.state = 480; + this.operationMode(); + this.state = 482; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + if (_la === 51) { + { + this.state = 481; this.eventClause(); } } - this.state = 383; + this.state = 484; this.match(QuixosCapabilityParser.RECEIVER); - this.state = 384; + this.state = 485; this.receiverRequirement(); - this.state = 386; + this.state = 487; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 48) { + if (_la === 53) { { - this.state = 385; + this.state = 486; this.dependencyBlock(); } } - this.state = 388; + this.state = 489; this.match(QuixosCapabilityParser.SEMI); } } @@ -1368,38 +1789,48 @@ export class QuixosCapabilityParser extends antlr.Parser { } public packageFunctionExport(): PackageFunctionExportContext { let localContext = new PackageFunctionExportContext(this.context, this.state); - this.enterRule(localContext, 42, QuixosCapabilityParser.RULE_packageFunctionExport); + this.enterRule(localContext, 54, QuixosCapabilityParser.RULE_packageFunctionExport); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 390; + this.state = 491; this.match(QuixosCapabilityParser.FUNCTION); - this.state = 391; + this.state = 492; this.identifier(); - this.state = 392; - this.match(QuixosCapabilityParser.ID); - this.state = 393; - this.stringLiteral(); - this.state = 394; - this.match(QuixosCapabilityParser.COLON); - this.state = 395; - this.valueType(); - this.state = 396; - this.match(QuixosCapabilityParser.ARROW); - this.state = 397; - this.valueType(); - this.state = 399; + this.state = 494; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 48) { + if (_la === 107) { { - this.state = 398; + this.state = 493; + this.typeParameters(); + } + } + + this.state = 496; + this.match(QuixosCapabilityParser.ID); + this.state = 497; + this.stringLiteral(); + this.state = 498; + this.match(QuixosCapabilityParser.COLON); + this.state = 499; + this.valueType(); + this.state = 500; + this.match(QuixosCapabilityParser.ARROW); + this.state = 501; + this.valueType(); + this.state = 503; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + if (_la === 53) { + { + this.state = 502; this.dependencyBlock(); } } - this.state = 401; + this.state = 505; this.match(QuixosCapabilityParser.SEMI); } } @@ -1418,38 +1849,38 @@ export class QuixosCapabilityParser extends antlr.Parser { } public packageConstructorExport(): PackageConstructorExportContext { let localContext = new PackageConstructorExportContext(this.context, this.state); - this.enterRule(localContext, 44, QuixosCapabilityParser.RULE_packageConstructorExport); + this.enterRule(localContext, 56, QuixosCapabilityParser.RULE_packageConstructorExport); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 403; + this.state = 507; this.match(QuixosCapabilityParser.CONSTRUCTOR); - this.state = 404; + this.state = 508; this.identifier(); - this.state = 405; + this.state = 509; this.match(QuixosCapabilityParser.ID); - this.state = 406; + this.state = 510; this.stringLiteral(); - this.state = 407; + this.state = 511; this.match(QuixosCapabilityParser.CONSTRUCTS); - this.state = 408; + this.state = 512; this.identifier(); - this.state = 409; + this.state = 513; this.match(QuixosCapabilityParser.COLON); - this.state = 410; + this.state = 514; this.valueType(); - this.state = 412; + this.state = 516; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 48) { + if (_la === 53) { { - this.state = 411; + this.state = 515; this.dependencyBlock(); } } - this.state = 414; + this.state = 518; this.match(QuixosCapabilityParser.SEMI); } } @@ -1468,13 +1899,13 @@ export class QuixosCapabilityParser extends antlr.Parser { } public eventClause(): EventClauseContext { let localContext = new EventClauseContext(this.context, this.state); - this.enterRule(localContext, 46, QuixosCapabilityParser.RULE_eventClause); + this.enterRule(localContext, 58, QuixosCapabilityParser.RULE_eventClause); try { this.enterOuterAlt(localContext, 1); { - this.state = 416; + this.state = 520; this.match(QuixosCapabilityParser.EMITS); - this.state = 417; + this.state = 521; this.valueType(); } } @@ -1493,14 +1924,14 @@ export class QuixosCapabilityParser extends antlr.Parser { } public operationMode(): OperationModeContext { let localContext = new OperationModeContext(this.context, this.state); - this.enterRule(localContext, 48, QuixosCapabilityParser.RULE_operationMode); + this.enterRule(localContext, 60, QuixosCapabilityParser.RULE_operationMode); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 419; + this.state = 523; _la = this.tokenStream.LA(1); - if(!(((((_la - 60)) & ~0x1F) === 0 && ((1 << (_la - 60)) & 31) !== 0))) { + if(!(((((_la - 65)) & ~0x1F) === 0 && ((1 << (_la - 65)) & 31) !== 0))) { this.errorHandler.recoverInline(this); } else { @@ -1524,46 +1955,71 @@ export class QuixosCapabilityParser extends antlr.Parser { } public receiverRequirement(): ReceiverRequirementContext { let localContext = new ReceiverRequirementContext(this.context, this.state); - this.enterRule(localContext, 50, QuixosCapabilityParser.RULE_receiverRequirement); + this.enterRule(localContext, 62, QuixosCapabilityParser.RULE_receiverRequirement); let _la: number; try { - this.state = 430; + this.state = 543; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.ANY: this.enterOuterAlt(localContext, 1); { - this.state = 421; + this.state = 525; this.match(QuixosCapabilityParser.ANY); } break; case QuixosCapabilityParser.ATOM: this.enterOuterAlt(localContext, 2); { - this.state = 422; + this.state = 526; this.match(QuixosCapabilityParser.ATOM); - this.state = 423; + this.state = 527; + this.identifier(); + } + break; + case QuixosCapabilityParser.OBJECT: + this.enterOuterAlt(localContext, 3); + { + this.state = 528; + this.match(QuixosCapabilityParser.OBJECT); + this.state = 529; this.identifier(); } break; case QuixosCapabilityParser.INTERFACES: - this.enterOuterAlt(localContext, 3); + this.enterOuterAlt(localContext, 4); { - this.state = 424; + this.state = 530; this.match(QuixosCapabilityParser.INTERFACES); - this.state = 425; + this.state = 531; this.match(QuixosCapabilityParser.LBRACK); - this.state = 427; + this.state = 540; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 34 || _la === 106) { + if (_la === 39 || _la === 113) { { - this.state = 426; - this.identifierList(); + this.state = 532; + this.interfaceType(); + this.state = 537; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + while (_la === 99) { + { + { + this.state = 533; + this.match(QuixosCapabilityParser.COMMA); + this.state = 534; + this.interfaceType(); + } + } + this.state = 539; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + } } } - this.state = 429; + this.state = 542; this.match(QuixosCapabilityParser.RBRACK); } break; @@ -1586,26 +2042,26 @@ export class QuixosCapabilityParser extends antlr.Parser { } public identifierList(): IdentifierListContext { let localContext = new IdentifierListContext(this.context, this.state); - this.enterRule(localContext, 52, QuixosCapabilityParser.RULE_identifierList); + this.enterRule(localContext, 64, QuixosCapabilityParser.RULE_identifierList); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 432; + this.state = 545; this.identifier(); - this.state = 437; + this.state = 550; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (_la === 94) { + while (_la === 99) { { { - this.state = 433; + this.state = 546; this.match(QuixosCapabilityParser.COMMA); - this.state = 434; + this.state = 547; this.identifier(); } } - this.state = 439; + this.state = 552; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } @@ -1626,30 +2082,30 @@ export class QuixosCapabilityParser extends antlr.Parser { } public dependencyBlock(): DependencyBlockContext { let localContext = new DependencyBlockContext(this.context, this.state); - this.enterRule(localContext, 54, QuixosCapabilityParser.RULE_dependencyBlock); + this.enterRule(localContext, 66, QuixosCapabilityParser.RULE_dependencyBlock); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 440; + this.state = 553; this.match(QuixosCapabilityParser.REQUIRES); - this.state = 441; + this.state = 554; this.match(QuixosCapabilityParser.LBRACE); - this.state = 445; + this.state = 558; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 12591168) !== 0)) { + while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 402917376) !== 0)) { { { - this.state = 442; + this.state = 555; this.dependencyPort(); } } - this.state = 447; + this.state = 560; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 448; + this.state = 561; this.match(QuixosCapabilityParser.RBRACE); } } @@ -1668,103 +2124,113 @@ export class QuixosCapabilityParser extends antlr.Parser { } public dependencyPort(): DependencyPortContext { let localContext = new DependencyPortContext(this.context, this.state); - this.enterRule(localContext, 56, QuixosCapabilityParser.RULE_dependencyPort); + this.enterRule(localContext, 68, QuixosCapabilityParser.RULE_dependencyPort); let _la: number; try { - this.state = 489; + this.state = 605; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.STATE: this.enterOuterAlt(localContext, 1); { - this.state = 450; + this.state = 563; this.match(QuixosCapabilityParser.STATE); - this.state = 451; + this.state = 564; this.identifier(); - this.state = 452; + this.state = 565; this.match(QuixosCapabilityParser.ID); - this.state = 453; + this.state = 566; this.stringLiteral(); - this.state = 454; + this.state = 567; this.match(QuixosCapabilityParser.COLON); - this.state = 455; + this.state = 568; this.valueType(); - this.state = 456; + this.state = 569; this.primitiveList(); - this.state = 457; + this.state = 570; this.match(QuixosCapabilityParser.SEMI); } break; case QuixosCapabilityParser.EDGE: this.enterOuterAlt(localContext, 2); { - this.state = 459; + this.state = 572; this.match(QuixosCapabilityParser.EDGE); - this.state = 460; + this.state = 573; this.identifier(); - this.state = 461; + this.state = 574; this.match(QuixosCapabilityParser.ID); - this.state = 462; + this.state = 575; this.stringLiteral(); - this.state = 463; + this.state = 576; this.match(QuixosCapabilityParser.COLON); - this.state = 464; + this.state = 577; this.cardinality(); - this.state = 465; + this.state = 578; this.targetConstraint(); - this.state = 466; + this.state = 579; this.primitiveList(); - this.state = 467; + this.state = 580; this.match(QuixosCapabilityParser.SEMI); } break; case QuixosCapabilityParser.INTERFACE: this.enterOuterAlt(localContext, 3); { - this.state = 469; + this.state = 582; this.match(QuixosCapabilityParser.INTERFACE); - this.state = 470; + this.state = 583; this.identifier(); - this.state = 471; + this.state = 584; this.match(QuixosCapabilityParser.ID); - this.state = 472; + this.state = 585; this.stringLiteral(); - this.state = 473; + this.state = 586; this.match(QuixosCapabilityParser.COLON); - this.state = 474; + this.state = 587; this.identifier(); - this.state = 475; + this.state = 589; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + if (_la === 107) { + { + this.state = 588; + this.typeArguments(); + } + } + + this.state = 591; this.match(QuixosCapabilityParser.SEMI); } break; case QuixosCapabilityParser.CONSTRUCTOR: this.enterOuterAlt(localContext, 4); { - this.state = 477; + this.state = 593; this.match(QuixosCapabilityParser.CONSTRUCTOR); - this.state = 478; + this.state = 594; this.identifier(); - this.state = 479; + this.state = 595; this.match(QuixosCapabilityParser.ID); - this.state = 480; + this.state = 596; this.stringLiteral(); - this.state = 481; + this.state = 597; this.match(QuixosCapabilityParser.COLON); - this.state = 482; + this.state = 598; this.identifier(); - this.state = 485; + this.state = 601; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 15) { + if (_la === 20) { { - this.state = 483; + this.state = 599; this.match(QuixosCapabilityParser.INPUT); - this.state = 484; + this.state = 600; this.valueType(); } } - this.state = 487; + this.state = 603; this.match(QuixosCapabilityParser.SEMI); } break; @@ -1787,32 +2253,32 @@ export class QuixosCapabilityParser extends antlr.Parser { } public primitiveList(): PrimitiveListContext { let localContext = new PrimitiveListContext(this.context, this.state); - this.enterRule(localContext, 58, QuixosCapabilityParser.RULE_primitiveList); + this.enterRule(localContext, 70, QuixosCapabilityParser.RULE_primitiveList); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 491; + this.state = 607; this.match(QuixosCapabilityParser.LBRACK); - this.state = 492; + this.state = 608; this.primitive(); - this.state = 497; + this.state = 613; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (_la === 94) { + while (_la === 99) { { { - this.state = 493; + this.state = 609; this.match(QuixosCapabilityParser.COMMA); - this.state = 494; + this.state = 610; this.primitive(); } } - this.state = 499; + this.state = 615; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 500; + this.state = 616; this.match(QuixosCapabilityParser.RBRACK); } } @@ -1831,14 +2297,14 @@ export class QuixosCapabilityParser extends antlr.Parser { } public primitive(): PrimitiveContext { let localContext = new PrimitiveContext(this.context, this.state); - this.enterRule(localContext, 60, QuixosCapabilityParser.RULE_primitive); + this.enterRule(localContext, 72, QuixosCapabilityParser.RULE_primitive); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 502; + this.state = 618; _la = this.tokenStream.LA(1); - if(!(((((_la - 55)) & ~0x1F) === 0 && ((1 << (_la - 55)) & 223) !== 0))) { + if(!(((((_la - 60)) & ~0x1F) === 0 && ((1 << (_la - 60)) & 223) !== 0))) { this.errorHandler.recoverInline(this); } else { @@ -1862,13 +2328,13 @@ export class QuixosCapabilityParser extends antlr.Parser { } public sharedAttachmentDecl(): SharedAttachmentDeclContext { let localContext = new SharedAttachmentDeclContext(this.context, this.state); - this.enterRule(localContext, 62, QuixosCapabilityParser.RULE_sharedAttachmentDecl); + this.enterRule(localContext, 74, QuixosCapabilityParser.RULE_sharedAttachmentDecl); try { this.enterOuterAlt(localContext, 1); { - this.state = 504; + this.state = 620; this.match(QuixosCapabilityParser.SHARED); - this.state = 505; + this.state = 621; this.attachmentDecl(); } } @@ -1887,22 +2353,22 @@ export class QuixosCapabilityParser extends antlr.Parser { } public attachmentDecl(): AttachmentDeclContext { let localContext = new AttachmentDeclContext(this.context, this.state); - this.enterRule(localContext, 64, QuixosCapabilityParser.RULE_attachmentDecl); + this.enterRule(localContext, 76, QuixosCapabilityParser.RULE_attachmentDecl); try { - this.state = 509; + this.state = 625; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.STATE: this.enterOuterAlt(localContext, 1); { - this.state = 507; + this.state = 623; this.stateDecl(); } break; case QuixosCapabilityParser.EDGE: this.enterOuterAlt(localContext, 2); { - this.state = 508; + this.state = 624; this.edgeDecl(); } break; @@ -1925,44 +2391,44 @@ export class QuixosCapabilityParser extends antlr.Parser { } public stateDecl(): StateDeclContext { let localContext = new StateDeclContext(this.context, this.state); - this.enterRule(localContext, 66, QuixosCapabilityParser.RULE_stateDecl); + this.enterRule(localContext, 78, QuixosCapabilityParser.RULE_stateDecl); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 511; + this.state = 627; this.match(QuixosCapabilityParser.STATE); - this.state = 512; + this.state = 628; this.identifier(); - this.state = 513; + this.state = 629; this.match(QuixosCapabilityParser.ID); - this.state = 514; + this.state = 630; this.stringLiteral(); - this.state = 515; + this.state = 631; this.match(QuixosCapabilityParser.ON); - this.state = 516; + this.state = 632; this.identifier(); - this.state = 517; + this.state = 633; this.match(QuixosCapabilityParser.COLON); - this.state = 518; + this.state = 634; this.valueType(); - this.state = 519; + this.state = 635; this.match(QuixosCapabilityParser.POLICY); - this.state = 520; + this.state = 636; this.storagePolicy(); - this.state = 523; + this.state = 639; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 33) { + if (_la === 38) { { - this.state = 521; + this.state = 637; this.match(QuixosCapabilityParser.DEFAULT); - this.state = 522; + this.state = 638; this.jsonLiteral(); } } - this.state = 525; + this.state = 641; this.match(QuixosCapabilityParser.SEMI); } } @@ -1981,28 +2447,28 @@ export class QuixosCapabilityParser extends antlr.Parser { } public storagePolicy(): StoragePolicyContext { let localContext = new StoragePolicyContext(this.context, this.state); - this.enterRule(localContext, 68, QuixosCapabilityParser.RULE_storagePolicy); + this.enterRule(localContext, 80, QuixosCapabilityParser.RULE_storagePolicy); try { - this.state = 533; + this.state = 649; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.OPTIMISTIC_REGISTER: this.enterOuterAlt(localContext, 1); { - this.state = 527; + this.state = 643; this.match(QuixosCapabilityParser.OPTIMISTIC_REGISTER); } break; case QuixosCapabilityParser.CRDT: this.enterOuterAlt(localContext, 2); { - this.state = 528; + this.state = 644; this.match(QuixosCapabilityParser.CRDT); - this.state = 529; + this.state = 645; this.match(QuixosCapabilityParser.LPAREN); - this.state = 530; + this.state = 646; this.valueType(); - this.state = 531; + this.state = 647; this.match(QuixosCapabilityParser.RPAREN); } break; @@ -2025,25 +2491,25 @@ export class QuixosCapabilityParser extends antlr.Parser { } public edgeDecl(): EdgeDeclContext { let localContext = new EdgeDeclContext(this.context, this.state); - this.enterRule(localContext, 70, QuixosCapabilityParser.RULE_edgeDecl); + this.enterRule(localContext, 82, QuixosCapabilityParser.RULE_edgeDecl); try { this.enterOuterAlt(localContext, 1); { - this.state = 535; + this.state = 651; this.match(QuixosCapabilityParser.EDGE); - this.state = 536; + this.state = 652; this.identifier(); - this.state = 537; + this.state = 653; this.match(QuixosCapabilityParser.ID); - this.state = 538; + this.state = 654; this.stringLiteral(); - this.state = 539; + this.state = 655; this.match(QuixosCapabilityParser.LBRACE); - this.state = 540; + this.state = 656; this.edgeEndpoint(); - this.state = 541; + this.state = 657; this.edgeEndpoint(); - this.state = 542; + this.state = 658; this.match(QuixosCapabilityParser.RBRACE); } } @@ -2062,78 +2528,78 @@ export class QuixosCapabilityParser extends antlr.Parser { } public edgeEndpoint(): EdgeEndpointContext { let localContext = new EdgeEndpointContext(this.context, this.state); - this.enterRule(localContext, 72, QuixosCapabilityParser.RULE_edgeEndpoint); + this.enterRule(localContext, 84, QuixosCapabilityParser.RULE_edgeEndpoint); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 544; + this.state = 660; this.targetConstraint(); - this.state = 545; + this.state = 661; this.match(QuixosCapabilityParser.PROJECTION); - this.state = 546; + this.state = 662; this.identifier(); - this.state = 547; + this.state = 663; this.match(QuixosCapabilityParser.ID); - this.state = 548; + this.state = 664; this.stringLiteral(); - this.state = 549; + this.state = 665; this.cardinality(); - this.state = 551; + this.state = 667; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 71) { + if (_la === 76) { { - this.state = 550; + this.state = 666; this.match(QuixosCapabilityParser.ORDERED); } } - this.state = 555; + this.state = 671; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 39) { + if (_la === 44) { { - this.state = 553; + this.state = 669; this.match(QuixosCapabilityParser.ON_DELETE); - this.state = 554; + this.state = 670; this.stringLiteral(); } } - this.state = 558; + this.state = 674; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 40) { + if (_la === 45) { { - this.state = 557; + this.state = 673; this.match(QuixosCapabilityParser.RETAIN_OTHER); } } - this.state = 562; + this.state = 678; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 41) { + if (_la === 46) { { - this.state = 560; + this.state = 676; this.match(QuixosCapabilityParser.KEYED); - this.state = 561; + this.state = 677; this.stringLiteral(); } } - this.state = 565; + this.state = 681; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 42) { + if (_la === 47) { { - this.state = 564; + this.state = 680; this.match(QuixosCapabilityParser.PUBLIC_TRAVERSAL); } } - this.state = 567; + this.state = 683; this.match(QuixosCapabilityParser.SEMI); } } @@ -2152,60 +2618,70 @@ export class QuixosCapabilityParser extends antlr.Parser { } public conformanceDecl(): ConformanceDeclContext { let localContext = new ConformanceDeclContext(this.context, this.state); - this.enterRule(localContext, 74, QuixosCapabilityParser.RULE_conformanceDecl); + this.enterRule(localContext, 86, QuixosCapabilityParser.RULE_conformanceDecl); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 569; + this.state = 685; this.match(QuixosCapabilityParser.CONFORM); - this.state = 570; + this.state = 686; this.identifier(); - this.state = 571; + this.state = 687; this.match(QuixosCapabilityParser.AS); - this.state = 572; + this.state = 688; this.identifier(); - this.state = 575; + this.state = 690; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 43) { + if (_la === 107) { { - this.state = 573; + this.state = 689; + this.typeArguments(); + } + } + + this.state = 694; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + if (_la === 48) { + { + this.state = 692; this.match(QuixosCapabilityParser.ID); - this.state = 574; + this.state = 693; this.stringLiteral(); } } - this.state = 579; + this.state = 698; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 38) { + if (_la === 43) { { - this.state = 577; + this.state = 696; this.match(QuixosCapabilityParser.SEMANTIC_MAJOR); - this.state = 578; + this.state = 697; this.match(QuixosCapabilityParser.INTEGER); } } - this.state = 581; + this.state = 700; this.match(QuixosCapabilityParser.LBRACE); - this.state = 585; + this.state = 704; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 269746176) !== 0)) { + while (((((_la - 23)) & ~0x1F) === 0 && ((1 << (_la - 23)) & 1029) !== 0)) { { { - this.state = 582; + this.state = 701; this.conformanceItem(); } } - this.state = 587; + this.state = 706; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 588; + this.state = 707; this.match(QuixosCapabilityParser.RBRACE); } } @@ -2224,31 +2700,31 @@ export class QuixosCapabilityParser extends antlr.Parser { } public conformanceItem(): ConformanceItemContext { let localContext = new ConformanceItemContext(this.context, this.state); - this.enterRule(localContext, 76, QuixosCapabilityParser.RULE_conformanceItem); + this.enterRule(localContext, 88, QuixosCapabilityParser.RULE_conformanceItem); try { - this.state = 594; + this.state = 713; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.PRIVATE: this.enterOuterAlt(localContext, 1); { - this.state = 590; + this.state = 709; this.match(QuixosCapabilityParser.PRIVATE); - this.state = 591; + this.state = 710; this.attachmentDecl(); } break; case QuixosCapabilityParser.BIND: this.enterOuterAlt(localContext, 2); { - this.state = 592; + this.state = 711; this.operationBindingDecl(); } break; case QuixosCapabilityParser.MATERIALIZE: this.enterOuterAlt(localContext, 3); { - this.state = 593; + this.state = 712; this.relationshipMaterializationDecl(); } break; @@ -2271,35 +2747,35 @@ export class QuixosCapabilityParser extends antlr.Parser { } public relationshipMaterializationDecl(): RelationshipMaterializationDeclContext { let localContext = new RelationshipMaterializationDeclContext(this.context, this.state); - this.enterRule(localContext, 78, QuixosCapabilityParser.RULE_relationshipMaterializationDecl); + this.enterRule(localContext, 90, QuixosCapabilityParser.RULE_relationshipMaterializationDecl); try { this.enterOuterAlt(localContext, 1); { - this.state = 596; + this.state = 715; this.match(QuixosCapabilityParser.MATERIALIZE); - this.state = 597; + this.state = 716; this.identifier(); - this.state = 598; + this.state = 717; this.match(QuixosCapabilityParser.IF); - this.state = 599; + this.state = 718; this.match(QuixosCapabilityParser.ABSENT); - this.state = 600; + this.state = 719; this.match(QuixosCapabilityParser.USING); - this.state = 601; + this.state = 720; this.match(QuixosCapabilityParser.CONSTRUCTOR); - this.state = 602; + this.state = 721; this.identifier(); - this.state = 603; + this.state = 722; this.match(QuixosCapabilityParser.VIA); - this.state = 604; + this.state = 723; this.match(QuixosCapabilityParser.EDGE); - this.state = 605; + this.state = 724; this.identifier(); - this.state = 606; + this.state = 725; this.match(QuixosCapabilityParser.DOT); - this.state = 607; + this.state = 726; this.identifier(); - this.state = 608; + this.state = 727; this.match(QuixosCapabilityParser.SEMI); } } @@ -2318,19 +2794,19 @@ export class QuixosCapabilityParser extends antlr.Parser { } public operationBindingDecl(): OperationBindingDeclContext { let localContext = new OperationBindingDeclContext(this.context, this.state); - this.enterRule(localContext, 80, QuixosCapabilityParser.RULE_operationBindingDecl); + this.enterRule(localContext, 92, QuixosCapabilityParser.RULE_operationBindingDecl); try { this.enterOuterAlt(localContext, 1); { - this.state = 610; + this.state = 729; this.match(QuixosCapabilityParser.BIND); - this.state = 611; + this.state = 730; this.memberOperationRef(); - this.state = 612; + this.state = 731; this.match(QuixosCapabilityParser.TO); - this.state = 613; + this.state = 732; this.operationProvider(); - this.state = 614; + this.state = 733; this.match(QuixosCapabilityParser.SEMI); } } @@ -2349,15 +2825,15 @@ export class QuixosCapabilityParser extends antlr.Parser { } public memberOperationRef(): MemberOperationRefContext { let localContext = new MemberOperationRefContext(this.context, this.state); - this.enterRule(localContext, 82, QuixosCapabilityParser.RULE_memberOperationRef); + this.enterRule(localContext, 94, QuixosCapabilityParser.RULE_memberOperationRef); try { this.enterOuterAlt(localContext, 1); { - this.state = 616; + this.state = 735; this.identifier(); - this.state = 617; + this.state = 736; this.match(QuixosCapabilityParser.DOT); - this.state = 618; + this.state = 737; this.operationName(); } } @@ -2376,86 +2852,86 @@ export class QuixosCapabilityParser extends antlr.Parser { } public operationName(): OperationNameContext { let localContext = new OperationNameContext(this.context, this.state); - this.enterRule(localContext, 84, QuixosCapabilityParser.RULE_operationName); + this.enterRule(localContext, 96, QuixosCapabilityParser.RULE_operationName); try { - this.state = 631; + this.state = 750; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.SOURCE: case QuixosCapabilityParser.IDENTIFIER: this.enterOuterAlt(localContext, 1); { - this.state = 620; + this.state = 739; this.identifier(); } break; case QuixosCapabilityParser.CALL: this.enterOuterAlt(localContext, 2); { - this.state = 621; + this.state = 740; this.match(QuixosCapabilityParser.CALL); } break; case QuixosCapabilityParser.GET: this.enterOuterAlt(localContext, 3); { - this.state = 622; + this.state = 741; this.match(QuixosCapabilityParser.GET); } break; case QuixosCapabilityParser.SET: this.enterOuterAlt(localContext, 4); { - this.state = 623; + this.state = 742; this.match(QuixosCapabilityParser.SET); } break; case QuixosCapabilityParser.RESOLVE: this.enterOuterAlt(localContext, 5); { - this.state = 624; + this.state = 743; this.match(QuixosCapabilityParser.RESOLVE); } break; case QuixosCapabilityParser.CONNECT: this.enterOuterAlt(localContext, 6); { - this.state = 625; + this.state = 744; this.match(QuixosCapabilityParser.CONNECT); } break; case QuixosCapabilityParser.DISCONNECT: this.enterOuterAlt(localContext, 7); { - this.state = 626; + this.state = 745; this.match(QuixosCapabilityParser.DISCONNECT); } break; case QuixosCapabilityParser.WATCH_START: this.enterOuterAlt(localContext, 8); { - this.state = 627; + this.state = 746; this.match(QuixosCapabilityParser.WATCH_START); } break; case QuixosCapabilityParser.WATCH_STOP: this.enterOuterAlt(localContext, 9); { - this.state = 628; + this.state = 747; this.match(QuixosCapabilityParser.WATCH_STOP); } break; case QuixosCapabilityParser.SUBSCRIBE: this.enterOuterAlt(localContext, 10); { - this.state = 629; + this.state = 748; this.match(QuixosCapabilityParser.SUBSCRIBE); } break; case QuixosCapabilityParser.UNSUBSCRIBE: this.enterOuterAlt(localContext, 11); { - this.state = 630; + this.state = 749; this.match(QuixosCapabilityParser.UNSUBSCRIBE); } break; @@ -2478,59 +2954,69 @@ export class QuixosCapabilityParser extends antlr.Parser { } public operationProvider(): OperationProviderContext { let localContext = new OperationProviderContext(this.context, this.state); - this.enterRule(localContext, 86, QuixosCapabilityParser.RULE_operationProvider); + this.enterRule(localContext, 98, QuixosCapabilityParser.RULE_operationProvider); let _la: number; try { - this.state = 652; + this.state = 774; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.STATE: this.enterOuterAlt(localContext, 1); { - this.state = 633; + this.state = 752; this.match(QuixosCapabilityParser.STATE); - this.state = 634; + this.state = 753; this.identifier(); - this.state = 635; + this.state = 754; this.match(QuixosCapabilityParser.DOT); - this.state = 636; + this.state = 755; this.statePrimitive(); } break; case QuixosCapabilityParser.EDGE: this.enterOuterAlt(localContext, 2); { - this.state = 638; + this.state = 757; this.match(QuixosCapabilityParser.EDGE); - this.state = 639; + this.state = 758; this.identifier(); - this.state = 640; + this.state = 759; this.match(QuixosCapabilityParser.DOT); - this.state = 641; + this.state = 760; this.identifier(); - this.state = 642; + this.state = 761; this.match(QuixosCapabilityParser.DOT); - this.state = 643; + this.state = 762; this.edgePrimitive(); } break; case QuixosCapabilityParser.PACKAGE: this.enterOuterAlt(localContext, 3); { - this.state = 645; + this.state = 764; this.match(QuixosCapabilityParser.PACKAGE); - this.state = 646; + this.state = 765; this.identifier(); - this.state = 647; + this.state = 766; this.match(QuixosCapabilityParser.DOT); - this.state = 648; + this.state = 767; this.identifier(); - this.state = 650; + this.state = 769; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 25) { + if (_la === 107) { { - this.state = 649; + this.state = 768; + this.typeArguments(); + } + } + + this.state = 772; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + if (_la === 30) { + { + this.state = 771; this.dependencyBindingBlock(); } } @@ -2556,14 +3042,14 @@ export class QuixosCapabilityParser extends antlr.Parser { } public statePrimitive(): StatePrimitiveContext { let localContext = new StatePrimitiveContext(this.context, this.state); - this.enterRule(localContext, 88, QuixosCapabilityParser.RULE_statePrimitive); + this.enterRule(localContext, 100, QuixosCapabilityParser.RULE_statePrimitive); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 654; + this.state = 776; _la = this.tokenStream.LA(1); - if(!(((((_la - 55)) & ~0x1F) === 0 && ((1 << (_la - 55)) & 195) !== 0))) { + if(!(((((_la - 60)) & ~0x1F) === 0 && ((1 << (_la - 60)) & 195) !== 0))) { this.errorHandler.recoverInline(this); } else { @@ -2587,14 +3073,14 @@ export class QuixosCapabilityParser extends antlr.Parser { } public edgePrimitive(): EdgePrimitiveContext { let localContext = new EdgePrimitiveContext(this.context, this.state); - this.enterRule(localContext, 90, QuixosCapabilityParser.RULE_edgePrimitive); + this.enterRule(localContext, 102, QuixosCapabilityParser.RULE_edgePrimitive); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 656; + this.state = 778; _la = this.tokenStream.LA(1); - if(!(((((_la - 57)) & ~0x1F) === 0 && ((1 << (_la - 57)) & 55) !== 0))) { + if(!(((((_la - 62)) & ~0x1F) === 0 && ((1 << (_la - 62)) & 55) !== 0))) { this.errorHandler.recoverInline(this); } else { @@ -2618,30 +3104,30 @@ export class QuixosCapabilityParser extends antlr.Parser { } public dependencyBindingBlock(): DependencyBindingBlockContext { let localContext = new DependencyBindingBlockContext(this.context, this.state); - this.enterRule(localContext, 92, QuixosCapabilityParser.RULE_dependencyBindingBlock); + this.enterRule(localContext, 104, QuixosCapabilityParser.RULE_dependencyBindingBlock); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 658; + this.state = 780; this.match(QuixosCapabilityParser.WITH); - this.state = 659; + this.state = 781; this.match(QuixosCapabilityParser.LBRACE); - this.state = 663; + this.state = 785; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (_la === 34 || _la === 106) { + while (_la === 39 || _la === 113) { { { - this.state = 660; + this.state = 782; this.dependencyBinding(); } } - this.state = 665; + this.state = 787; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 666; + this.state = 788; this.match(QuixosCapabilityParser.RBRACE); } } @@ -2660,127 +3146,137 @@ export class QuixosCapabilityParser extends antlr.Parser { } public dependencyBinding(): DependencyBindingContext { let localContext = new DependencyBindingContext(this.context, this.state); - this.enterRule(localContext, 94, QuixosCapabilityParser.RULE_dependencyBinding); + this.enterRule(localContext, 106, QuixosCapabilityParser.RULE_dependencyBinding); let _la: number; try { - this.state = 718; + this.state = 843; this.errorHandler.sync(this); - switch (this.interpreter.adaptivePredict(this.tokenStream, 50, this.context) ) { + switch (this.interpreter.adaptivePredict(this.tokenStream, 70, this.context) ) { case 1: this.enterOuterAlt(localContext, 1); { - this.state = 668; + this.state = 790; this.identifier(); - this.state = 669; + this.state = 791; this.match(QuixosCapabilityParser.TO); - this.state = 670; + this.state = 792; this.match(QuixosCapabilityParser.STATE); - this.state = 671; + this.state = 793; this.identifier(); - this.state = 678; + this.state = 800; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 27) { + if (_la === 32) { { - this.state = 672; + this.state = 794; this.match(QuixosCapabilityParser.VIA); - this.state = 673; + this.state = 795; this.match(QuixosCapabilityParser.EDGE); - this.state = 674; + this.state = 796; this.identifier(); - this.state = 675; + this.state = 797; this.match(QuixosCapabilityParser.DOT); - this.state = 676; + this.state = 798; this.identifier(); } } - this.state = 680; + this.state = 802; this.match(QuixosCapabilityParser.SEMI); } break; case 2: this.enterOuterAlt(localContext, 2); { - this.state = 682; + this.state = 804; this.identifier(); - this.state = 683; + this.state = 805; this.match(QuixosCapabilityParser.TO); - this.state = 684; + this.state = 806; this.match(QuixosCapabilityParser.EDGE); - this.state = 685; + this.state = 807; this.identifier(); - this.state = 686; + this.state = 808; this.match(QuixosCapabilityParser.DOT); - this.state = 687; + this.state = 809; this.identifier(); - this.state = 694; + this.state = 816; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 27) { + if (_la === 32) { { - this.state = 688; + this.state = 810; this.match(QuixosCapabilityParser.VIA); - this.state = 689; + this.state = 811; this.match(QuixosCapabilityParser.EDGE); - this.state = 690; + this.state = 812; this.identifier(); - this.state = 691; + this.state = 813; this.match(QuixosCapabilityParser.DOT); - this.state = 692; + this.state = 814; this.identifier(); } } - this.state = 696; + this.state = 818; this.match(QuixosCapabilityParser.SEMI); } break; case 3: this.enterOuterAlt(localContext, 3); { - this.state = 698; + this.state = 820; this.identifier(); - this.state = 699; + this.state = 821; this.match(QuixosCapabilityParser.TO); - this.state = 700; + this.state = 822; this.match(QuixosCapabilityParser.INTERFACE); - this.state = 701; + this.state = 823; this.identifier(); - this.state = 708; + this.state = 825; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 27) { + if (_la === 107) { { - this.state = 702; + this.state = 824; + this.typeArguments(); + } + } + + this.state = 833; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + if (_la === 32) { + { + this.state = 827; this.match(QuixosCapabilityParser.VIA); - this.state = 703; + this.state = 828; this.match(QuixosCapabilityParser.EDGE); - this.state = 704; + this.state = 829; this.identifier(); - this.state = 705; + this.state = 830; this.match(QuixosCapabilityParser.DOT); - this.state = 706; + this.state = 831; this.identifier(); } } - this.state = 710; + this.state = 835; this.match(QuixosCapabilityParser.SEMI); } break; case 4: this.enterOuterAlt(localContext, 4); { - this.state = 712; + this.state = 837; this.identifier(); - this.state = 713; + this.state = 838; this.match(QuixosCapabilityParser.TO); - this.state = 714; + this.state = 839; this.match(QuixosCapabilityParser.CONSTRUCTOR); - this.state = 715; + this.state = 840; this.identifier(); - this.state = 716; + this.state = 841; this.match(QuixosCapabilityParser.SEMI); } break; @@ -2801,34 +3297,34 @@ export class QuixosCapabilityParser extends antlr.Parser { } public constructorBindingDecl(): ConstructorBindingDeclContext { let localContext = new ConstructorBindingDeclContext(this.context, this.state); - this.enterRule(localContext, 96, QuixosCapabilityParser.RULE_constructorBindingDecl); + this.enterRule(localContext, 108, QuixosCapabilityParser.RULE_constructorBindingDecl); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 720; + this.state = 845; this.match(QuixosCapabilityParser.CONSTRUCTOR); - this.state = 721; + this.state = 846; this.identifier(); - this.state = 722; + this.state = 847; this.match(QuixosCapabilityParser.TO); - this.state = 723; + this.state = 848; this.identifier(); - this.state = 724; + this.state = 849; this.match(QuixosCapabilityParser.DOT); - this.state = 725; + this.state = 850; this.identifier(); - this.state = 727; + this.state = 852; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 25) { + if (_la === 30) { { - this.state = 726; + this.state = 851; this.dependencyBindingBlock(); } } - this.state = 729; + this.state = 854; this.match(QuixosCapabilityParser.SEMI); } } @@ -2847,10 +3343,10 @@ export class QuixosCapabilityParser extends antlr.Parser { } public valueType(): ValueTypeContext { let localContext = new ValueTypeContext(this.context, this.state); - this.enterRule(localContext, 98, QuixosCapabilityParser.RULE_valueType); + this.enterRule(localContext, 110, QuixosCapabilityParser.RULE_valueType); let _la: number; try { - this.state = 765; + this.state = 902; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.BOOL: @@ -2863,108 +3359,149 @@ export class QuixosCapabilityParser extends antlr.Parser { case QuixosCapabilityParser.UINT64: this.enterOuterAlt(localContext, 1); { - this.state = 731; + this.state = 856; this.scalarType(); } break; case QuixosCapabilityParser.UNIT: this.enterOuterAlt(localContext, 2); { - this.state = 732; + this.state = 857; this.match(QuixosCapabilityParser.UNIT); } break; case QuixosCapabilityParser.WATCH_HANDLE: this.enterOuterAlt(localContext, 3); { - this.state = 733; + this.state = 858; this.match(QuixosCapabilityParser.WATCH_HANDLE); } break; case QuixosCapabilityParser.MESSAGE: this.enterOuterAlt(localContext, 4); { - this.state = 734; + this.state = 859; this.match(QuixosCapabilityParser.MESSAGE); - this.state = 735; + this.state = 860; this.stringLiteral(); } break; case QuixosCapabilityParser.ATOM_REF: this.enterOuterAlt(localContext, 5); { - this.state = 736; + this.state = 861; this.match(QuixosCapabilityParser.ATOM_REF); - this.state = 737; + this.state = 862; this.match(QuixosCapabilityParser.LT); - this.state = 738; + this.state = 863; this.identifier(); - this.state = 739; + this.state = 864; this.match(QuixosCapabilityParser.GT); } break; case QuixosCapabilityParser.INTERFACE_REF: this.enterOuterAlt(localContext, 6); { - this.state = 741; + this.state = 866; this.match(QuixosCapabilityParser.INTERFACE_REF); - this.state = 742; + this.state = 867; this.match(QuixosCapabilityParser.LT); - this.state = 743; + this.state = 868; this.identifier(); - this.state = 744; + this.state = 870; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + if (_la === 107) { + { + this.state = 869; + this.typeArguments(); + } + } + + this.state = 872; + this.match(QuixosCapabilityParser.GT); + } + break; + case QuixosCapabilityParser.REF: + this.enterOuterAlt(localContext, 7); + { + this.state = 874; + this.match(QuixosCapabilityParser.REF); + this.state = 875; + this.match(QuixosCapabilityParser.LT); + this.state = 876; + this.identifier(); + this.state = 877; this.match(QuixosCapabilityParser.GT); } break; case QuixosCapabilityParser.OPTIONAL: - this.enterOuterAlt(localContext, 7); + this.enterOuterAlt(localContext, 8); { - this.state = 746; + this.state = 879; this.match(QuixosCapabilityParser.OPTIONAL); - this.state = 747; + this.state = 880; this.match(QuixosCapabilityParser.LT); - this.state = 748; + this.state = 881; this.valueType(); - this.state = 749; + this.state = 882; this.match(QuixosCapabilityParser.GT); } break; case QuixosCapabilityParser.LIST: - this.enterOuterAlt(localContext, 8); + this.enterOuterAlt(localContext, 9); { - this.state = 751; + this.state = 884; this.match(QuixosCapabilityParser.LIST); - this.state = 752; + this.state = 885; this.match(QuixosCapabilityParser.LT); - this.state = 753; + this.state = 886; this.valueType(); - this.state = 754; + this.state = 887; this.match(QuixosCapabilityParser.GT); } break; case QuixosCapabilityParser.RECORD: - this.enterOuterAlt(localContext, 9); + this.enterOuterAlt(localContext, 10); { - this.state = 756; + this.state = 889; this.match(QuixosCapabilityParser.RECORD); - this.state = 757; + this.state = 890; this.match(QuixosCapabilityParser.LBRACE); - this.state = 761; + this.state = 894; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (_la === 34 || _la === 106) { + while (_la === 39 || _la === 113) { { { - this.state = 758; + this.state = 891; this.recordField(); } } - this.state = 763; + this.state = 896; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 764; + this.state = 897; this.match(QuixosCapabilityParser.RBRACE); + } + break; + case QuixosCapabilityParser.SOURCE: + case QuixosCapabilityParser.IDENTIFIER: + this.enterOuterAlt(localContext, 11); + { + this.state = 898; + this.identifier(); + this.state = 900; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + if (_la === 107) { + { + this.state = 899; + this.typeArguments(); + } + } + } break; default: @@ -2986,17 +3523,17 @@ export class QuixosCapabilityParser extends antlr.Parser { } public recordField(): RecordFieldContext { let localContext = new RecordFieldContext(this.context, this.state); - this.enterRule(localContext, 100, QuixosCapabilityParser.RULE_recordField); + this.enterRule(localContext, 112, QuixosCapabilityParser.RULE_recordField); try { this.enterOuterAlt(localContext, 1); { - this.state = 767; + this.state = 904; this.identifier(); - this.state = 768; + this.state = 905; this.match(QuixosCapabilityParser.COLON); - this.state = 769; + this.state = 906; this.valueType(); - this.state = 770; + this.state = 907; this.match(QuixosCapabilityParser.SEMI); } } @@ -3015,14 +3552,14 @@ export class QuixosCapabilityParser extends antlr.Parser { } public scalarType(): ScalarTypeContext { let localContext = new ScalarTypeContext(this.context, this.state); - this.enterRule(localContext, 102, QuixosCapabilityParser.RULE_scalarType); + this.enterRule(localContext, 114, QuixosCapabilityParser.RULE_scalarType); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 772; + this.state = 909; _la = this.tokenStream.LA(1); - if(!(((((_la - 80)) & ~0x1F) === 0 && ((1 << (_la - 80)) & 255) !== 0))) { + if(!(((((_la - 85)) & ~0x1F) === 0 && ((1 << (_la - 85)) & 255) !== 0))) { this.errorHandler.recoverInline(this); } else { @@ -3046,14 +3583,14 @@ export class QuixosCapabilityParser extends antlr.Parser { } public cardinality(): CardinalityContext { let localContext = new CardinalityContext(this.context, this.state); - this.enterRule(localContext, 104, QuixosCapabilityParser.RULE_cardinality); + this.enterRule(localContext, 116, QuixosCapabilityParser.RULE_cardinality); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 774; + this.state = 911; _la = this.tokenStream.LA(1); - if(!(((((_la - 67)) & ~0x1F) === 0 && ((1 << (_la - 67)) & 15) !== 0))) { + if(!(((((_la - 72)) & ~0x1F) === 0 && ((1 << (_la - 72)) & 15) !== 0))) { this.errorHandler.recoverInline(this); } else { @@ -3077,64 +3614,64 @@ export class QuixosCapabilityParser extends antlr.Parser { } public jsonLiteral(): JsonLiteralContext { let localContext = new JsonLiteralContext(this.context, this.state); - this.enterRule(localContext, 106, QuixosCapabilityParser.RULE_jsonLiteral); + this.enterRule(localContext, 118, QuixosCapabilityParser.RULE_jsonLiteral); try { - this.state = 784; + this.state = 921; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.STRING_LITERAL: this.enterOuterAlt(localContext, 1); { - this.state = 776; + this.state = 913; this.stringLiteral(); } break; case QuixosCapabilityParser.INTEGER: this.enterOuterAlt(localContext, 2); { - this.state = 777; + this.state = 914; this.match(QuixosCapabilityParser.INTEGER); } break; case QuixosCapabilityParser.JSON_NUMBER: this.enterOuterAlt(localContext, 3); { - this.state = 778; + this.state = 915; this.match(QuixosCapabilityParser.JSON_NUMBER); } break; case QuixosCapabilityParser.TRUE: this.enterOuterAlt(localContext, 4); { - this.state = 779; + this.state = 916; this.match(QuixosCapabilityParser.TRUE); } break; case QuixosCapabilityParser.FALSE: this.enterOuterAlt(localContext, 5); { - this.state = 780; + this.state = 917; this.match(QuixosCapabilityParser.FALSE); } break; case QuixosCapabilityParser.NULL: this.enterOuterAlt(localContext, 6); { - this.state = 781; + this.state = 918; this.match(QuixosCapabilityParser.NULL); } break; case QuixosCapabilityParser.LBRACE: this.enterOuterAlt(localContext, 7); { - this.state = 782; + this.state = 919; this.jsonObject(); } break; case QuixosCapabilityParser.LBRACK: this.enterOuterAlt(localContext, 8); { - this.state = 783; + this.state = 920; this.jsonArray(); } break; @@ -3157,40 +3694,40 @@ export class QuixosCapabilityParser extends antlr.Parser { } public jsonObject(): JsonObjectContext { let localContext = new JsonObjectContext(this.context, this.state); - this.enterRule(localContext, 108, QuixosCapabilityParser.RULE_jsonObject); + this.enterRule(localContext, 120, QuixosCapabilityParser.RULE_jsonObject); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 786; + this.state = 923; this.match(QuixosCapabilityParser.LBRACE); - this.state = 795; + this.state = 932; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 107) { + if (_la === 114) { { - this.state = 787; + this.state = 924; this.jsonMember(); - this.state = 792; + this.state = 929; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (_la === 94) { + while (_la === 99) { { { - this.state = 788; + this.state = 925; this.match(QuixosCapabilityParser.COMMA); - this.state = 789; + this.state = 926; this.jsonMember(); } } - this.state = 794; + this.state = 931; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } } } - this.state = 797; + this.state = 934; this.match(QuixosCapabilityParser.RBRACE); } } @@ -3209,15 +3746,15 @@ export class QuixosCapabilityParser extends antlr.Parser { } public jsonMember(): JsonMemberContext { let localContext = new JsonMemberContext(this.context, this.state); - this.enterRule(localContext, 110, QuixosCapabilityParser.RULE_jsonMember); + this.enterRule(localContext, 122, QuixosCapabilityParser.RULE_jsonMember); try { this.enterOuterAlt(localContext, 1); { - this.state = 799; + this.state = 936; this.stringLiteral(); - this.state = 800; + this.state = 937; this.match(QuixosCapabilityParser.COLON); - this.state = 801; + this.state = 938; this.jsonLiteral(); } } @@ -3236,40 +3773,40 @@ export class QuixosCapabilityParser extends antlr.Parser { } public jsonArray(): JsonArrayContext { let localContext = new JsonArrayContext(this.context, this.state); - this.enterRule(localContext, 112, QuixosCapabilityParser.RULE_jsonArray); + this.enterRule(localContext, 124, QuixosCapabilityParser.RULE_jsonArray); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 803; + this.state = 940; this.match(QuixosCapabilityParser.LBRACK); - this.state = 812; + this.state = 949; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (((((_la - 88)) & ~0x1F) === 0 && ((1 << (_la - 88)) & 722183) !== 0)) { + if (((((_la - 93)) & ~0x1F) === 0 && ((1 << (_la - 93)) & 2884871) !== 0)) { { - this.state = 804; + this.state = 941; this.jsonLiteral(); - this.state = 809; + this.state = 946; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (_la === 94) { + while (_la === 99) { { { - this.state = 805; + this.state = 942; this.match(QuixosCapabilityParser.COMMA); - this.state = 806; + this.state = 943; this.jsonLiteral(); } } - this.state = 811; + this.state = 948; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } } } - this.state = 814; + this.state = 951; this.match(QuixosCapabilityParser.RBRACK); } } @@ -3288,14 +3825,14 @@ export class QuixosCapabilityParser extends antlr.Parser { } public identifier(): IdentifierContext { let localContext = new IdentifierContext(this.context, this.state); - this.enterRule(localContext, 114, QuixosCapabilityParser.RULE_identifier); + this.enterRule(localContext, 126, QuixosCapabilityParser.RULE_identifier); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 816; + this.state = 953; _la = this.tokenStream.LA(1); - if(!(_la === 34 || _la === 106)) { + if(!(_la === 39 || _la === 113)) { this.errorHandler.recoverInline(this); } else { @@ -3319,11 +3856,11 @@ export class QuixosCapabilityParser extends antlr.Parser { } public stringLiteral(): StringLiteralContext { let localContext = new StringLiteralContext(this.context, this.state); - this.enterRule(localContext, 116, QuixosCapabilityParser.RULE_stringLiteral); + this.enterRule(localContext, 128, QuixosCapabilityParser.RULE_stringLiteral); try { this.enterOuterAlt(localContext, 1); { - this.state = 818; + this.state = 955; this.match(QuixosCapabilityParser.STRING_LITERAL); } } @@ -3342,7 +3879,7 @@ export class QuixosCapabilityParser extends antlr.Parser { } public static readonly _serializedATN: number[] = [ - 4,1,110,821,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6, + 4,1,117,958,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6, 7,6,2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7, 13,2,14,7,14,2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19,7,19,2, 20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25,2,26,7, @@ -3350,296 +3887,352 @@ export class QuixosCapabilityParser extends antlr.Parser { 33,7,33,2,34,7,34,2,35,7,35,2,36,7,36,2,37,7,37,2,38,7,38,2,39,7, 39,2,40,7,40,2,41,7,41,2,42,7,42,2,43,7,43,2,44,7,44,2,45,7,45,2, 46,7,46,2,47,7,47,2,48,7,48,2,49,7,49,2,50,7,50,2,51,7,51,2,52,7, - 52,2,53,7,53,2,54,7,54,2,55,7,55,2,56,7,56,2,57,7,57,2,58,7,58,1, - 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,3,0,131,8,0,1,1,1, - 1,1,1,5,1,136,8,1,10,1,12,1,139,9,1,1,1,1,1,1,2,1,2,1,2,1,2,1,3, - 1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,5,3,157,8,3,10,3,12,3,160,9, - 3,1,3,1,3,1,4,1,4,1,4,1,4,1,4,1,4,3,4,170,8,4,1,5,1,5,1,5,1,5,1, - 5,1,5,1,5,1,5,1,5,1,5,3,5,182,8,5,1,6,1,6,1,6,1,6,1,6,1,6,1,6,1, - 7,1,7,1,7,1,7,1,7,1,7,1,7,1,8,1,8,1,8,3,8,201,8,8,1,9,1,9,1,9,1, - 9,1,9,1,9,3,9,209,8,9,1,9,1,9,1,10,5,10,214,8,10,10,10,12,10,217, - 9,10,1,10,1,10,1,10,1,10,1,10,1,10,1,10,1,10,5,10,227,8,10,10,10, - 12,10,230,9,10,1,10,1,10,1,11,1,11,1,11,3,11,237,8,11,1,12,1,12, - 1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12, - 1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,5,13,262,8,13,10,13,12,13, - 265,9,13,1,13,1,13,1,14,1,14,1,14,1,14,1,14,1,14,1,14,1,14,1,14, - 1,14,1,14,1,14,1,14,1,14,1,14,1,14,1,14,1,14,1,14,3,14,288,8,14, - 1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,15,3,15,298,8,15,1,15,1,15, - 5,15,302,8,15,10,15,12,15,305,9,15,1,15,1,15,1,16,1,16,1,16,1,16, - 1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16, - 1,16,1,16,1,16,1,16,1,16,1,16,1,16,3,16,333,8,16,1,17,1,17,1,17, - 1,17,3,17,339,8,17,1,18,5,18,342,8,18,10,18,12,18,345,9,18,1,18, - 1,18,1,18,1,18,1,18,1,18,1,18,1,18,3,18,355,8,18,1,18,1,18,5,18, - 359,8,18,10,18,12,18,362,9,18,1,18,1,18,1,19,1,19,1,19,3,19,369, - 8,19,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,3,20, - 382,8,20,1,20,1,20,1,20,3,20,387,8,20,1,20,1,20,1,21,1,21,1,21,1, - 21,1,21,1,21,1,21,1,21,1,21,3,21,400,8,21,1,21,1,21,1,22,1,22,1, - 22,1,22,1,22,1,22,1,22,1,22,1,22,3,22,413,8,22,1,22,1,22,1,23,1, - 23,1,23,1,24,1,24,1,25,1,25,1,25,1,25,1,25,1,25,3,25,428,8,25,1, - 25,3,25,431,8,25,1,26,1,26,1,26,5,26,436,8,26,10,26,12,26,439,9, - 26,1,27,1,27,1,27,5,27,444,8,27,10,27,12,27,447,9,27,1,27,1,27,1, - 28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1, - 28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1, - 28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,3,28,486,8,28,1,28,1, - 28,3,28,490,8,28,1,29,1,29,1,29,1,29,5,29,496,8,29,10,29,12,29,499, - 9,29,1,29,1,29,1,30,1,30,1,31,1,31,1,31,1,32,1,32,3,32,510,8,32, - 1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,3,33, - 524,8,33,1,33,1,33,1,34,1,34,1,34,1,34,1,34,1,34,3,34,534,8,34,1, - 35,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,36,1,36,1,36,1,36,1, - 36,1,36,1,36,3,36,552,8,36,1,36,1,36,3,36,556,8,36,1,36,3,36,559, - 8,36,1,36,1,36,3,36,563,8,36,1,36,3,36,566,8,36,1,36,1,36,1,37,1, - 37,1,37,1,37,1,37,1,37,3,37,576,8,37,1,37,1,37,3,37,580,8,37,1,37, - 1,37,5,37,584,8,37,10,37,12,37,587,9,37,1,37,1,37,1,38,1,38,1,38, - 1,38,3,38,595,8,38,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39, - 1,39,1,39,1,39,1,39,1,39,1,40,1,40,1,40,1,40,1,40,1,40,1,41,1,41, - 1,41,1,41,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42, - 3,42,632,8,42,1,43,1,43,1,43,1,43,1,43,1,43,1,43,1,43,1,43,1,43, - 1,43,1,43,1,43,1,43,1,43,1,43,1,43,3,43,651,8,43,3,43,653,8,43,1, - 44,1,44,1,45,1,45,1,46,1,46,1,46,5,46,662,8,46,10,46,12,46,665,9, - 46,1,46,1,46,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1,47,3, - 47,679,8,47,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1, - 47,1,47,1,47,1,47,3,47,695,8,47,1,47,1,47,1,47,1,47,1,47,1,47,1, - 47,1,47,1,47,1,47,1,47,1,47,3,47,709,8,47,1,47,1,47,1,47,1,47,1, - 47,1,47,1,47,1,47,3,47,719,8,47,1,48,1,48,1,48,1,48,1,48,1,48,1, - 48,3,48,728,8,48,1,48,1,48,1,49,1,49,1,49,1,49,1,49,1,49,1,49,1, - 49,1,49,1,49,1,49,1,49,1,49,1,49,1,49,1,49,1,49,1,49,1,49,1,49,1, - 49,1,49,1,49,1,49,1,49,1,49,1,49,1,49,5,49,760,8,49,10,49,12,49, - 763,9,49,1,49,3,49,766,8,49,1,50,1,50,1,50,1,50,1,50,1,51,1,51,1, - 52,1,52,1,53,1,53,1,53,1,53,1,53,1,53,1,53,1,53,3,53,785,8,53,1, - 54,1,54,1,54,1,54,5,54,791,8,54,10,54,12,54,794,9,54,3,54,796,8, - 54,1,54,1,54,1,55,1,55,1,55,1,55,1,56,1,56,1,56,1,56,5,56,808,8, - 56,10,56,12,56,811,9,56,3,56,813,8,56,1,56,1,56,1,57,1,57,1,58,1, - 58,1,58,0,0,59,0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34, - 36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78, - 80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116, - 0,7,1,0,60,64,2,0,55,59,61,62,2,0,55,56,61,62,2,0,57,59,61,62,1, - 0,80,87,1,0,67,70,2,0,34,34,106,106,861,0,130,1,0,0,0,2,132,1,0, - 0,0,4,142,1,0,0,0,6,146,1,0,0,0,8,169,1,0,0,0,10,181,1,0,0,0,12, - 183,1,0,0,0,14,190,1,0,0,0,16,200,1,0,0,0,18,202,1,0,0,0,20,215, - 1,0,0,0,22,236,1,0,0,0,24,238,1,0,0,0,26,253,1,0,0,0,28,287,1,0, - 0,0,30,289,1,0,0,0,32,332,1,0,0,0,34,338,1,0,0,0,36,343,1,0,0,0, - 38,368,1,0,0,0,40,370,1,0,0,0,42,390,1,0,0,0,44,403,1,0,0,0,46,416, - 1,0,0,0,48,419,1,0,0,0,50,430,1,0,0,0,52,432,1,0,0,0,54,440,1,0, - 0,0,56,489,1,0,0,0,58,491,1,0,0,0,60,502,1,0,0,0,62,504,1,0,0,0, - 64,509,1,0,0,0,66,511,1,0,0,0,68,533,1,0,0,0,70,535,1,0,0,0,72,544, - 1,0,0,0,74,569,1,0,0,0,76,594,1,0,0,0,78,596,1,0,0,0,80,610,1,0, - 0,0,82,616,1,0,0,0,84,631,1,0,0,0,86,652,1,0,0,0,88,654,1,0,0,0, - 90,656,1,0,0,0,92,658,1,0,0,0,94,718,1,0,0,0,96,720,1,0,0,0,98,765, - 1,0,0,0,100,767,1,0,0,0,102,772,1,0,0,0,104,774,1,0,0,0,106,784, - 1,0,0,0,108,786,1,0,0,0,110,799,1,0,0,0,112,803,1,0,0,0,114,816, - 1,0,0,0,116,818,1,0,0,0,118,119,3,6,3,0,119,120,5,0,0,1,120,131, - 1,0,0,0,121,122,3,20,10,0,122,123,5,0,0,1,123,131,1,0,0,0,124,125, - 3,36,18,0,125,126,5,0,0,1,126,131,1,0,0,0,127,128,3,2,1,0,128,129, - 5,0,0,1,129,131,1,0,0,0,130,118,1,0,0,0,130,121,1,0,0,0,130,124, - 1,0,0,0,130,127,1,0,0,0,131,1,1,0,0,0,132,133,5,2,0,0,133,137,5, - 96,0,0,134,136,3,8,4,0,135,134,1,0,0,0,136,139,1,0,0,0,137,135,1, - 0,0,0,137,138,1,0,0,0,138,140,1,0,0,0,139,137,1,0,0,0,140,141,5, - 97,0,0,141,3,1,0,0,0,142,143,5,3,0,0,143,144,3,116,58,0,144,145, - 5,93,0,0,145,5,1,0,0,0,146,147,5,1,0,0,147,148,3,114,57,0,148,149, - 5,43,0,0,149,150,3,116,58,0,150,151,5,37,0,0,151,152,3,116,58,0, - 152,153,5,36,0,0,153,154,3,116,58,0,154,158,5,96,0,0,155,157,3,8, - 4,0,156,155,1,0,0,0,157,160,1,0,0,0,158,156,1,0,0,0,158,159,1,0, - 0,0,159,161,1,0,0,0,160,158,1,0,0,0,161,162,5,97,0,0,162,7,1,0,0, - 0,163,170,3,4,2,0,164,170,3,18,9,0,165,170,3,10,5,0,166,170,3,62, - 31,0,167,170,3,74,37,0,168,170,3,96,48,0,169,163,1,0,0,0,169,164, - 1,0,0,0,169,165,1,0,0,0,169,166,1,0,0,0,169,167,1,0,0,0,169,168, - 1,0,0,0,170,9,1,0,0,0,171,172,5,3,0,0,172,173,5,6,0,0,173,174,3, - 114,57,0,174,175,5,93,0,0,175,182,1,0,0,0,176,177,5,3,0,0,177,178, - 5,8,0,0,178,179,3,114,57,0,179,180,5,93,0,0,180,182,1,0,0,0,181, - 171,1,0,0,0,181,176,1,0,0,0,182,11,1,0,0,0,183,184,5,4,0,0,184,185, - 5,5,0,0,185,186,3,114,57,0,186,187,5,43,0,0,187,188,3,116,58,0,188, - 189,5,93,0,0,189,13,1,0,0,0,190,191,5,4,0,0,191,192,5,6,0,0,192, - 193,3,114,57,0,193,194,5,37,0,0,194,195,3,116,58,0,195,196,5,93, - 0,0,196,15,1,0,0,0,197,201,3,10,5,0,198,201,3,12,6,0,199,201,3,14, - 7,0,200,197,1,0,0,0,200,198,1,0,0,0,200,199,1,0,0,0,201,17,1,0,0, - 0,202,203,5,5,0,0,203,204,3,114,57,0,204,205,5,43,0,0,205,208,3, - 116,58,0,206,207,5,44,0,0,207,209,3,116,58,0,208,206,1,0,0,0,208, - 209,1,0,0,0,209,210,1,0,0,0,210,211,5,93,0,0,211,19,1,0,0,0,212, - 214,3,16,8,0,213,212,1,0,0,0,214,217,1,0,0,0,215,213,1,0,0,0,215, - 216,1,0,0,0,216,218,1,0,0,0,217,215,1,0,0,0,218,219,5,6,0,0,219, - 220,3,114,57,0,220,221,5,43,0,0,221,222,3,116,58,0,222,223,5,37, - 0,0,223,224,3,116,58,0,224,228,5,96,0,0,225,227,3,22,11,0,226,225, - 1,0,0,0,227,230,1,0,0,0,228,226,1,0,0,0,228,229,1,0,0,0,229,231, - 1,0,0,0,230,228,1,0,0,0,231,232,5,97,0,0,232,21,1,0,0,0,233,237, - 3,26,13,0,234,237,3,30,15,0,235,237,3,24,12,0,236,233,1,0,0,0,236, - 234,1,0,0,0,236,235,1,0,0,0,237,23,1,0,0,0,238,239,5,11,0,0,239, - 240,3,114,57,0,240,241,5,43,0,0,241,242,3,116,58,0,242,243,5,92, - 0,0,243,244,3,98,49,0,244,245,5,91,0,0,245,246,3,98,49,0,246,247, - 5,96,0,0,247,248,5,60,0,0,248,249,5,43,0,0,249,250,3,116,58,0,250, - 251,5,93,0,0,251,252,5,97,0,0,252,25,1,0,0,0,253,254,5,9,0,0,254, - 255,3,114,57,0,255,256,5,43,0,0,256,257,3,116,58,0,257,258,5,92, - 0,0,258,259,3,98,49,0,259,263,5,96,0,0,260,262,3,28,14,0,261,260, - 1,0,0,0,262,265,1,0,0,0,263,261,1,0,0,0,263,264,1,0,0,0,264,266, - 1,0,0,0,265,263,1,0,0,0,266,267,5,97,0,0,267,27,1,0,0,0,268,269, - 5,50,0,0,269,270,5,43,0,0,270,271,3,116,58,0,271,272,5,93,0,0,272, - 288,1,0,0,0,273,274,5,51,0,0,274,275,5,43,0,0,275,276,3,116,58,0, - 276,277,5,93,0,0,277,288,1,0,0,0,278,279,5,52,0,0,279,280,5,53,0, - 0,280,281,5,43,0,0,281,282,3,116,58,0,282,283,5,54,0,0,283,284,5, - 43,0,0,284,285,3,116,58,0,285,286,5,93,0,0,286,288,1,0,0,0,287,268, - 1,0,0,0,287,273,1,0,0,0,287,278,1,0,0,0,288,29,1,0,0,0,289,290,5, - 10,0,0,290,291,3,114,57,0,291,292,5,43,0,0,292,293,3,116,58,0,293, - 294,5,92,0,0,294,295,3,104,52,0,295,297,3,34,17,0,296,298,5,71,0, - 0,297,296,1,0,0,0,297,298,1,0,0,0,298,299,1,0,0,0,299,303,5,96,0, - 0,300,302,3,32,16,0,301,300,1,0,0,0,302,305,1,0,0,0,303,301,1,0, - 0,0,303,304,1,0,0,0,304,306,1,0,0,0,305,303,1,0,0,0,306,307,5,97, - 0,0,307,31,1,0,0,0,308,309,5,57,0,0,309,310,5,43,0,0,310,311,3,116, - 58,0,311,312,5,93,0,0,312,333,1,0,0,0,313,314,5,58,0,0,314,315,5, - 43,0,0,315,316,3,116,58,0,316,317,5,93,0,0,317,333,1,0,0,0,318,319, - 5,59,0,0,319,320,5,43,0,0,320,321,3,116,58,0,321,322,5,93,0,0,322, - 333,1,0,0,0,323,324,5,52,0,0,324,325,5,53,0,0,325,326,5,43,0,0,326, - 327,3,116,58,0,327,328,5,54,0,0,328,329,5,43,0,0,329,330,3,116,58, - 0,330,331,5,93,0,0,331,333,1,0,0,0,332,308,1,0,0,0,332,313,1,0,0, - 0,332,318,1,0,0,0,332,323,1,0,0,0,333,33,1,0,0,0,334,335,5,5,0,0, - 335,339,3,114,57,0,336,337,5,6,0,0,337,339,3,114,57,0,338,334,1, - 0,0,0,338,336,1,0,0,0,339,35,1,0,0,0,340,342,3,16,8,0,341,340,1, - 0,0,0,342,345,1,0,0,0,343,341,1,0,0,0,343,344,1,0,0,0,344,346,1, - 0,0,0,345,343,1,0,0,0,346,347,5,8,0,0,347,348,3,114,57,0,348,349, - 5,43,0,0,349,350,3,116,58,0,350,351,5,37,0,0,351,354,3,116,58,0, - 352,353,5,38,0,0,353,355,5,104,0,0,354,352,1,0,0,0,354,355,1,0,0, - 0,355,356,1,0,0,0,356,360,5,96,0,0,357,359,3,38,19,0,358,357,1,0, - 0,0,359,362,1,0,0,0,360,358,1,0,0,0,360,361,1,0,0,0,361,363,1,0, - 0,0,362,360,1,0,0,0,363,364,5,97,0,0,364,37,1,0,0,0,365,369,3,40, - 20,0,366,369,3,42,21,0,367,369,3,44,22,0,368,365,1,0,0,0,368,366, - 1,0,0,0,368,367,1,0,0,0,369,39,1,0,0,0,370,371,5,11,0,0,371,372, - 3,114,57,0,372,373,5,43,0,0,373,374,3,116,58,0,374,375,5,92,0,0, - 375,376,3,98,49,0,376,377,5,91,0,0,377,378,3,98,49,0,378,379,5,45, - 0,0,379,381,3,48,24,0,380,382,3,46,23,0,381,380,1,0,0,0,381,382, - 1,0,0,0,382,383,1,0,0,0,383,384,5,47,0,0,384,386,3,50,25,0,385,387, - 3,54,27,0,386,385,1,0,0,0,386,387,1,0,0,0,387,388,1,0,0,0,388,389, - 5,93,0,0,389,41,1,0,0,0,390,391,5,12,0,0,391,392,3,114,57,0,392, - 393,5,43,0,0,393,394,3,116,58,0,394,395,5,92,0,0,395,396,3,98,49, - 0,396,397,5,91,0,0,397,399,3,98,49,0,398,400,3,54,27,0,399,398,1, - 0,0,0,399,400,1,0,0,0,400,401,1,0,0,0,401,402,5,93,0,0,402,43,1, - 0,0,0,403,404,5,13,0,0,404,405,3,114,57,0,405,406,5,43,0,0,406,407, - 3,116,58,0,407,408,5,14,0,0,408,409,3,114,57,0,409,410,5,92,0,0, - 410,412,3,98,49,0,411,413,3,54,27,0,412,411,1,0,0,0,412,413,1,0, - 0,0,413,414,1,0,0,0,414,415,5,93,0,0,415,45,1,0,0,0,416,417,5,46, - 0,0,417,418,3,98,49,0,418,47,1,0,0,0,419,420,7,0,0,0,420,49,1,0, - 0,0,421,431,5,49,0,0,422,423,5,5,0,0,423,431,3,114,57,0,424,425, - 5,7,0,0,425,427,5,98,0,0,426,428,3,52,26,0,427,426,1,0,0,0,427,428, - 1,0,0,0,428,429,1,0,0,0,429,431,5,99,0,0,430,421,1,0,0,0,430,422, - 1,0,0,0,430,424,1,0,0,0,431,51,1,0,0,0,432,437,3,114,57,0,433,434, - 5,94,0,0,434,436,3,114,57,0,435,433,1,0,0,0,436,439,1,0,0,0,437, - 435,1,0,0,0,437,438,1,0,0,0,438,53,1,0,0,0,439,437,1,0,0,0,440,441, - 5,48,0,0,441,445,5,96,0,0,442,444,3,56,28,0,443,442,1,0,0,0,444, - 447,1,0,0,0,445,443,1,0,0,0,445,446,1,0,0,0,446,448,1,0,0,0,447, - 445,1,0,0,0,448,449,5,97,0,0,449,55,1,0,0,0,450,451,5,22,0,0,451, - 452,3,114,57,0,452,453,5,43,0,0,453,454,3,116,58,0,454,455,5,92, - 0,0,455,456,3,98,49,0,456,457,3,58,29,0,457,458,5,93,0,0,458,490, - 1,0,0,0,459,460,5,23,0,0,460,461,3,114,57,0,461,462,5,43,0,0,462, - 463,3,116,58,0,463,464,5,92,0,0,464,465,3,104,52,0,465,466,3,34, - 17,0,466,467,3,58,29,0,467,468,5,93,0,0,468,490,1,0,0,0,469,470, - 5,6,0,0,470,471,3,114,57,0,471,472,5,43,0,0,472,473,3,116,58,0,473, - 474,5,92,0,0,474,475,3,114,57,0,475,476,5,93,0,0,476,490,1,0,0,0, - 477,478,5,13,0,0,478,479,3,114,57,0,479,480,5,43,0,0,480,481,3,116, - 58,0,481,482,5,92,0,0,482,485,3,114,57,0,483,484,5,15,0,0,484,486, - 3,98,49,0,485,483,1,0,0,0,485,486,1,0,0,0,486,487,1,0,0,0,487,488, - 5,93,0,0,488,490,1,0,0,0,489,450,1,0,0,0,489,459,1,0,0,0,489,469, - 1,0,0,0,489,477,1,0,0,0,490,57,1,0,0,0,491,492,5,98,0,0,492,497, - 3,60,30,0,493,494,5,94,0,0,494,496,3,60,30,0,495,493,1,0,0,0,496, - 499,1,0,0,0,497,495,1,0,0,0,497,498,1,0,0,0,498,500,1,0,0,0,499, - 497,1,0,0,0,500,501,5,99,0,0,501,59,1,0,0,0,502,503,7,1,0,0,503, - 61,1,0,0,0,504,505,5,21,0,0,505,506,3,64,32,0,506,63,1,0,0,0,507, - 510,3,66,33,0,508,510,3,70,35,0,509,507,1,0,0,0,509,508,1,0,0,0, - 510,65,1,0,0,0,511,512,5,22,0,0,512,513,3,114,57,0,513,514,5,43, - 0,0,514,515,3,116,58,0,515,516,5,31,0,0,516,517,3,114,57,0,517,518, - 5,92,0,0,518,519,3,98,49,0,519,520,5,32,0,0,520,523,3,68,34,0,521, - 522,5,33,0,0,522,524,3,106,53,0,523,521,1,0,0,0,523,524,1,0,0,0, - 524,525,1,0,0,0,525,526,5,93,0,0,526,67,1,0,0,0,527,534,5,65,0,0, - 528,529,5,66,0,0,529,530,5,100,0,0,530,531,3,98,49,0,531,532,5,101, - 0,0,532,534,1,0,0,0,533,527,1,0,0,0,533,528,1,0,0,0,534,69,1,0,0, - 0,535,536,5,23,0,0,536,537,3,114,57,0,537,538,5,43,0,0,538,539,3, - 116,58,0,539,540,5,96,0,0,540,541,3,72,36,0,541,542,3,72,36,0,542, - 543,5,97,0,0,543,71,1,0,0,0,544,545,3,34,17,0,545,546,5,24,0,0,546, - 547,3,114,57,0,547,548,5,43,0,0,548,549,3,116,58,0,549,551,3,104, - 52,0,550,552,5,71,0,0,551,550,1,0,0,0,551,552,1,0,0,0,552,555,1, - 0,0,0,553,554,5,39,0,0,554,556,3,116,58,0,555,553,1,0,0,0,555,556, - 1,0,0,0,556,558,1,0,0,0,557,559,5,40,0,0,558,557,1,0,0,0,558,559, - 1,0,0,0,559,562,1,0,0,0,560,561,5,41,0,0,561,563,3,116,58,0,562, - 560,1,0,0,0,562,563,1,0,0,0,563,565,1,0,0,0,564,566,5,42,0,0,565, - 564,1,0,0,0,565,566,1,0,0,0,566,567,1,0,0,0,567,568,5,93,0,0,568, - 73,1,0,0,0,569,570,5,16,0,0,570,571,3,114,57,0,571,572,5,17,0,0, - 572,575,3,114,57,0,573,574,5,43,0,0,574,576,3,116,58,0,575,573,1, - 0,0,0,575,576,1,0,0,0,576,579,1,0,0,0,577,578,5,38,0,0,578,580,5, - 104,0,0,579,577,1,0,0,0,579,580,1,0,0,0,580,581,1,0,0,0,581,585, - 5,96,0,0,582,584,3,76,38,0,583,582,1,0,0,0,584,587,1,0,0,0,585,583, - 1,0,0,0,585,586,1,0,0,0,586,588,1,0,0,0,587,585,1,0,0,0,588,589, - 5,97,0,0,589,75,1,0,0,0,590,591,5,20,0,0,591,595,3,64,32,0,592,595, - 3,80,40,0,593,595,3,78,39,0,594,590,1,0,0,0,594,592,1,0,0,0,594, - 593,1,0,0,0,595,77,1,0,0,0,596,597,5,28,0,0,597,598,3,114,57,0,598, - 599,5,29,0,0,599,600,5,30,0,0,600,601,5,26,0,0,601,602,5,13,0,0, - 602,603,3,114,57,0,603,604,5,27,0,0,604,605,5,23,0,0,605,606,3,114, - 57,0,606,607,5,95,0,0,607,608,3,114,57,0,608,609,5,93,0,0,609,79, - 1,0,0,0,610,611,5,18,0,0,611,612,3,82,41,0,612,613,5,19,0,0,613, - 614,3,86,43,0,614,615,5,93,0,0,615,81,1,0,0,0,616,617,3,114,57,0, - 617,618,5,95,0,0,618,619,3,84,42,0,619,83,1,0,0,0,620,632,3,114, - 57,0,621,632,5,60,0,0,622,632,5,50,0,0,623,632,5,51,0,0,624,632, - 5,57,0,0,625,632,5,58,0,0,626,632,5,59,0,0,627,632,5,61,0,0,628, - 632,5,62,0,0,629,632,5,63,0,0,630,632,5,64,0,0,631,620,1,0,0,0,631, - 621,1,0,0,0,631,622,1,0,0,0,631,623,1,0,0,0,631,624,1,0,0,0,631, - 625,1,0,0,0,631,626,1,0,0,0,631,627,1,0,0,0,631,628,1,0,0,0,631, - 629,1,0,0,0,631,630,1,0,0,0,632,85,1,0,0,0,633,634,5,22,0,0,634, - 635,3,114,57,0,635,636,5,95,0,0,636,637,3,88,44,0,637,653,1,0,0, - 0,638,639,5,23,0,0,639,640,3,114,57,0,640,641,5,95,0,0,641,642,3, - 114,57,0,642,643,5,95,0,0,643,644,3,90,45,0,644,653,1,0,0,0,645, - 646,5,8,0,0,646,647,3,114,57,0,647,648,5,95,0,0,648,650,3,114,57, - 0,649,651,3,92,46,0,650,649,1,0,0,0,650,651,1,0,0,0,651,653,1,0, - 0,0,652,633,1,0,0,0,652,638,1,0,0,0,652,645,1,0,0,0,653,87,1,0,0, - 0,654,655,7,2,0,0,655,89,1,0,0,0,656,657,7,3,0,0,657,91,1,0,0,0, - 658,659,5,25,0,0,659,663,5,96,0,0,660,662,3,94,47,0,661,660,1,0, - 0,0,662,665,1,0,0,0,663,661,1,0,0,0,663,664,1,0,0,0,664,666,1,0, - 0,0,665,663,1,0,0,0,666,667,5,97,0,0,667,93,1,0,0,0,668,669,3,114, - 57,0,669,670,5,19,0,0,670,671,5,22,0,0,671,678,3,114,57,0,672,673, - 5,27,0,0,673,674,5,23,0,0,674,675,3,114,57,0,675,676,5,95,0,0,676, - 677,3,114,57,0,677,679,1,0,0,0,678,672,1,0,0,0,678,679,1,0,0,0,679, - 680,1,0,0,0,680,681,5,93,0,0,681,719,1,0,0,0,682,683,3,114,57,0, - 683,684,5,19,0,0,684,685,5,23,0,0,685,686,3,114,57,0,686,687,5,95, - 0,0,687,694,3,114,57,0,688,689,5,27,0,0,689,690,5,23,0,0,690,691, - 3,114,57,0,691,692,5,95,0,0,692,693,3,114,57,0,693,695,1,0,0,0,694, - 688,1,0,0,0,694,695,1,0,0,0,695,696,1,0,0,0,696,697,5,93,0,0,697, - 719,1,0,0,0,698,699,3,114,57,0,699,700,5,19,0,0,700,701,5,6,0,0, - 701,708,3,114,57,0,702,703,5,27,0,0,703,704,5,23,0,0,704,705,3,114, - 57,0,705,706,5,95,0,0,706,707,3,114,57,0,707,709,1,0,0,0,708,702, - 1,0,0,0,708,709,1,0,0,0,709,710,1,0,0,0,710,711,5,93,0,0,711,719, - 1,0,0,0,712,713,3,114,57,0,713,714,5,19,0,0,714,715,5,13,0,0,715, - 716,3,114,57,0,716,717,5,93,0,0,717,719,1,0,0,0,718,668,1,0,0,0, - 718,682,1,0,0,0,718,698,1,0,0,0,718,712,1,0,0,0,719,95,1,0,0,0,720, - 721,5,13,0,0,721,722,3,114,57,0,722,723,5,19,0,0,723,724,3,114,57, - 0,724,725,5,95,0,0,725,727,3,114,57,0,726,728,3,92,46,0,727,726, - 1,0,0,0,727,728,1,0,0,0,728,729,1,0,0,0,729,730,5,93,0,0,730,97, - 1,0,0,0,731,766,3,102,51,0,732,766,5,72,0,0,733,766,5,73,0,0,734, - 735,5,74,0,0,735,766,3,116,58,0,736,737,5,75,0,0,737,738,5,102,0, - 0,738,739,3,114,57,0,739,740,5,103,0,0,740,766,1,0,0,0,741,742,5, - 76,0,0,742,743,5,102,0,0,743,744,3,114,57,0,744,745,5,103,0,0,745, - 766,1,0,0,0,746,747,5,77,0,0,747,748,5,102,0,0,748,749,3,98,49,0, - 749,750,5,103,0,0,750,766,1,0,0,0,751,752,5,78,0,0,752,753,5,102, - 0,0,753,754,3,98,49,0,754,755,5,103,0,0,755,766,1,0,0,0,756,757, - 5,79,0,0,757,761,5,96,0,0,758,760,3,100,50,0,759,758,1,0,0,0,760, - 763,1,0,0,0,761,759,1,0,0,0,761,762,1,0,0,0,762,764,1,0,0,0,763, - 761,1,0,0,0,764,766,5,97,0,0,765,731,1,0,0,0,765,732,1,0,0,0,765, - 733,1,0,0,0,765,734,1,0,0,0,765,736,1,0,0,0,765,741,1,0,0,0,765, - 746,1,0,0,0,765,751,1,0,0,0,765,756,1,0,0,0,766,99,1,0,0,0,767,768, - 3,114,57,0,768,769,5,92,0,0,769,770,3,98,49,0,770,771,5,93,0,0,771, - 101,1,0,0,0,772,773,7,4,0,0,773,103,1,0,0,0,774,775,7,5,0,0,775, - 105,1,0,0,0,776,785,3,116,58,0,777,785,5,104,0,0,778,785,5,105,0, - 0,779,785,5,88,0,0,780,785,5,89,0,0,781,785,5,90,0,0,782,785,3,108, - 54,0,783,785,3,112,56,0,784,776,1,0,0,0,784,777,1,0,0,0,784,778, - 1,0,0,0,784,779,1,0,0,0,784,780,1,0,0,0,784,781,1,0,0,0,784,782, - 1,0,0,0,784,783,1,0,0,0,785,107,1,0,0,0,786,795,5,96,0,0,787,792, - 3,110,55,0,788,789,5,94,0,0,789,791,3,110,55,0,790,788,1,0,0,0,791, - 794,1,0,0,0,792,790,1,0,0,0,792,793,1,0,0,0,793,796,1,0,0,0,794, - 792,1,0,0,0,795,787,1,0,0,0,795,796,1,0,0,0,796,797,1,0,0,0,797, - 798,5,97,0,0,798,109,1,0,0,0,799,800,3,116,58,0,800,801,5,92,0,0, - 801,802,3,106,53,0,802,111,1,0,0,0,803,812,5,98,0,0,804,809,3,106, - 53,0,805,806,5,94,0,0,806,808,3,106,53,0,807,805,1,0,0,0,808,811, - 1,0,0,0,809,807,1,0,0,0,809,810,1,0,0,0,810,813,1,0,0,0,811,809, - 1,0,0,0,812,804,1,0,0,0,812,813,1,0,0,0,813,814,1,0,0,0,814,815, - 5,99,0,0,815,113,1,0,0,0,816,817,7,6,0,0,817,115,1,0,0,0,818,819, - 5,107,0,0,819,117,1,0,0,0,59,130,137,158,169,181,200,208,215,228, - 236,263,287,297,303,332,338,343,354,360,368,381,386,399,412,427, - 430,437,445,485,489,497,509,523,533,551,555,558,562,565,575,579, - 585,594,631,650,652,663,678,694,708,718,727,761,765,784,792,795, - 809,812 + 52,2,53,7,53,2,54,7,54,2,55,7,55,2,56,7,56,2,57,7,57,2,58,7,58,2, + 59,7,59,2,60,7,60,2,61,7,61,2,62,7,62,2,63,7,63,2,64,7,64,1,0,1, + 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,3,0,143,8,0,1,1,1,1,1, + 1,5,1,148,8,1,10,1,12,1,151,9,1,1,1,1,1,1,2,1,2,1,2,1,2,1,3,1,3, + 1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,5,3,169,8,3,10,3,12,3,172,9,3,1, + 3,1,3,1,4,1,4,1,4,1,4,1,4,1,4,1,4,3,4,183,8,4,1,5,1,5,1,5,1,5,1, + 5,1,5,1,5,1,5,1,5,1,5,3,5,195,8,5,1,6,1,6,1,6,1,6,1,6,1,6,1,6,1, + 7,1,7,1,7,1,7,1,7,1,7,1,7,1,8,1,8,1,8,1,8,3,8,215,8,8,1,9,1,9,1, + 9,1,9,1,9,1,9,3,9,223,8,9,1,9,1,9,1,10,5,10,228,8,10,10,10,12,10, + 231,9,10,1,10,1,10,1,10,3,10,236,8,10,1,10,1,10,1,10,1,10,1,10,1, + 10,1,10,1,10,5,10,246,8,10,10,10,12,10,249,9,10,3,10,251,8,10,1, + 10,1,10,5,10,255,8,10,10,10,12,10,258,9,10,1,10,1,10,1,11,1,11,1, + 11,1,11,5,11,266,8,11,10,11,12,11,269,9,11,1,11,1,11,1,12,1,12,1, + 12,1,12,3,12,277,8,12,1,12,1,12,1,12,1,12,1,12,1,12,5,12,285,8,12, + 10,12,12,12,288,9,12,3,12,290,8,12,3,12,292,8,12,1,13,1,13,3,13, + 296,8,13,1,14,1,14,1,14,1,14,5,14,302,8,14,10,14,12,14,305,9,14, + 1,14,1,14,1,15,1,15,1,15,1,15,1,15,1,15,1,15,3,15,316,8,15,1,16, + 1,16,1,16,3,16,321,8,16,1,16,1,16,1,16,1,16,1,17,1,17,1,17,3,17, + 330,8,17,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,18, + 1,18,1,18,1,18,1,18,1,19,1,19,1,19,1,19,1,19,1,19,1,19,1,19,5,19, + 355,8,19,10,19,12,19,358,9,19,1,19,1,19,1,20,1,20,1,20,1,20,1,20, + 1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20, + 1,20,3,20,381,8,20,1,21,1,21,1,21,1,21,1,21,1,21,1,21,1,21,3,21, + 391,8,21,1,21,1,21,5,21,395,8,21,10,21,12,21,398,9,21,1,21,1,21, + 1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22, + 1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,3,22,426, + 8,22,1,23,1,23,1,23,1,23,1,23,3,23,433,8,23,1,23,1,23,3,23,437,8, + 23,1,24,5,24,440,8,24,10,24,12,24,443,9,24,1,24,1,24,1,24,1,24,1, + 24,1,24,1,24,1,24,3,24,453,8,24,1,24,1,24,5,24,457,8,24,10,24,12, + 24,460,9,24,1,24,1,24,1,25,1,25,1,25,3,25,467,8,25,1,26,1,26,1,26, + 3,26,472,8,26,1,26,1,26,1,26,1,26,1,26,1,26,1,26,1,26,1,26,3,26, + 483,8,26,1,26,1,26,1,26,3,26,488,8,26,1,26,1,26,1,27,1,27,1,27,3, + 27,495,8,27,1,27,1,27,1,27,1,27,1,27,1,27,1,27,3,27,504,8,27,1,27, + 1,27,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,3,28,517,8,28, + 1,28,1,28,1,29,1,29,1,29,1,30,1,30,1,31,1,31,1,31,1,31,1,31,1,31, + 1,31,1,31,1,31,1,31,5,31,536,8,31,10,31,12,31,539,9,31,3,31,541, + 8,31,1,31,3,31,544,8,31,1,32,1,32,1,32,5,32,549,8,32,10,32,12,32, + 552,9,32,1,33,1,33,1,33,5,33,557,8,33,10,33,12,33,560,9,33,1,33, + 1,33,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34, + 1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34, + 1,34,3,34,590,8,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34, + 1,34,3,34,602,8,34,1,34,1,34,3,34,606,8,34,1,35,1,35,1,35,1,35,5, + 35,612,8,35,10,35,12,35,615,9,35,1,35,1,35,1,36,1,36,1,37,1,37,1, + 37,1,38,1,38,3,38,626,8,38,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1, + 39,1,39,1,39,1,39,1,39,3,39,640,8,39,1,39,1,39,1,40,1,40,1,40,1, + 40,1,40,1,40,3,40,650,8,40,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1, + 41,1,41,1,42,1,42,1,42,1,42,1,42,1,42,1,42,3,42,668,8,42,1,42,1, + 42,3,42,672,8,42,1,42,3,42,675,8,42,1,42,1,42,3,42,679,8,42,1,42, + 3,42,682,8,42,1,42,1,42,1,43,1,43,1,43,1,43,1,43,3,43,691,8,43,1, + 43,1,43,3,43,695,8,43,1,43,1,43,3,43,699,8,43,1,43,1,43,5,43,703, + 8,43,10,43,12,43,706,9,43,1,43,1,43,1,44,1,44,1,44,1,44,3,44,714, + 8,44,1,45,1,45,1,45,1,45,1,45,1,45,1,45,1,45,1,45,1,45,1,45,1,45, + 1,45,1,45,1,46,1,46,1,46,1,46,1,46,1,46,1,47,1,47,1,47,1,47,1,48, + 1,48,1,48,1,48,1,48,1,48,1,48,1,48,1,48,1,48,1,48,3,48,751,8,48, + 1,49,1,49,1,49,1,49,1,49,1,49,1,49,1,49,1,49,1,49,1,49,1,49,1,49, + 1,49,1,49,1,49,1,49,3,49,770,8,49,1,49,3,49,773,8,49,3,49,775,8, + 49,1,50,1,50,1,51,1,51,1,52,1,52,1,52,5,52,784,8,52,10,52,12,52, + 787,9,52,1,52,1,52,1,53,1,53,1,53,1,53,1,53,1,53,1,53,1,53,1,53, + 1,53,3,53,801,8,53,1,53,1,53,1,53,1,53,1,53,1,53,1,53,1,53,1,53, + 1,53,1,53,1,53,1,53,1,53,3,53,817,8,53,1,53,1,53,1,53,1,53,1,53, + 1,53,1,53,3,53,826,8,53,1,53,1,53,1,53,1,53,1,53,1,53,3,53,834,8, + 53,1,53,1,53,1,53,1,53,1,53,1,53,1,53,1,53,3,53,844,8,53,1,54,1, + 54,1,54,1,54,1,54,1,54,1,54,3,54,853,8,54,1,54,1,54,1,55,1,55,1, + 55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,3,55,871, + 8,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55, + 1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,5,55,893,8,55,10,55,12,55, + 896,9,55,1,55,1,55,1,55,3,55,901,8,55,3,55,903,8,55,1,56,1,56,1, + 56,1,56,1,56,1,57,1,57,1,58,1,58,1,59,1,59,1,59,1,59,1,59,1,59,1, + 59,1,59,3,59,922,8,59,1,60,1,60,1,60,1,60,5,60,928,8,60,10,60,12, + 60,931,9,60,3,60,933,8,60,1,60,1,60,1,61,1,61,1,61,1,61,1,62,1,62, + 1,62,1,62,5,62,945,8,62,10,62,12,62,948,9,62,3,62,950,8,62,1,62, + 1,62,1,63,1,63,1,64,1,64,1,64,0,0,65,0,2,4,6,8,10,12,14,16,18,20, + 22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64, + 66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106, + 108,110,112,114,116,118,120,122,124,126,128,0,7,1,0,65,69,2,0,60, + 64,66,67,2,0,60,61,66,67,2,0,62,64,66,67,1,0,85,92,1,0,72,75,2,0, + 39,39,113,113,1022,0,142,1,0,0,0,2,144,1,0,0,0,4,154,1,0,0,0,6,158, + 1,0,0,0,8,182,1,0,0,0,10,194,1,0,0,0,12,196,1,0,0,0,14,203,1,0,0, + 0,16,214,1,0,0,0,18,216,1,0,0,0,20,229,1,0,0,0,22,261,1,0,0,0,24, + 291,1,0,0,0,26,293,1,0,0,0,28,297,1,0,0,0,30,315,1,0,0,0,32,317, + 1,0,0,0,34,329,1,0,0,0,36,331,1,0,0,0,38,346,1,0,0,0,40,380,1,0, + 0,0,42,382,1,0,0,0,44,425,1,0,0,0,46,436,1,0,0,0,48,441,1,0,0,0, + 50,466,1,0,0,0,52,468,1,0,0,0,54,491,1,0,0,0,56,507,1,0,0,0,58,520, + 1,0,0,0,60,523,1,0,0,0,62,543,1,0,0,0,64,545,1,0,0,0,66,553,1,0, + 0,0,68,605,1,0,0,0,70,607,1,0,0,0,72,618,1,0,0,0,74,620,1,0,0,0, + 76,625,1,0,0,0,78,627,1,0,0,0,80,649,1,0,0,0,82,651,1,0,0,0,84,660, + 1,0,0,0,86,685,1,0,0,0,88,713,1,0,0,0,90,715,1,0,0,0,92,729,1,0, + 0,0,94,735,1,0,0,0,96,750,1,0,0,0,98,774,1,0,0,0,100,776,1,0,0,0, + 102,778,1,0,0,0,104,780,1,0,0,0,106,843,1,0,0,0,108,845,1,0,0,0, + 110,902,1,0,0,0,112,904,1,0,0,0,114,909,1,0,0,0,116,911,1,0,0,0, + 118,921,1,0,0,0,120,923,1,0,0,0,122,936,1,0,0,0,124,940,1,0,0,0, + 126,953,1,0,0,0,128,955,1,0,0,0,130,131,3,6,3,0,131,132,5,0,0,1, + 132,143,1,0,0,0,133,134,3,20,10,0,134,135,5,0,0,1,135,143,1,0,0, + 0,136,137,3,48,24,0,137,138,5,0,0,1,138,143,1,0,0,0,139,140,3,2, + 1,0,140,141,5,0,0,1,141,143,1,0,0,0,142,130,1,0,0,0,142,133,1,0, + 0,0,142,136,1,0,0,0,142,139,1,0,0,0,143,1,1,0,0,0,144,145,5,7,0, + 0,145,149,5,101,0,0,146,148,3,8,4,0,147,146,1,0,0,0,148,151,1,0, + 0,0,149,147,1,0,0,0,149,150,1,0,0,0,150,152,1,0,0,0,151,149,1,0, + 0,0,152,153,5,102,0,0,153,3,1,0,0,0,154,155,5,8,0,0,155,156,3,128, + 64,0,156,157,5,98,0,0,157,5,1,0,0,0,158,159,5,1,0,0,159,160,3,126, + 63,0,160,161,5,48,0,0,161,162,3,128,64,0,162,163,5,42,0,0,163,164, + 3,128,64,0,164,165,5,41,0,0,165,166,3,128,64,0,166,170,5,101,0,0, + 167,169,3,8,4,0,168,167,1,0,0,0,169,172,1,0,0,0,170,168,1,0,0,0, + 170,171,1,0,0,0,171,173,1,0,0,0,172,170,1,0,0,0,173,174,5,102,0, + 0,174,7,1,0,0,0,175,183,3,4,2,0,176,183,3,18,9,0,177,183,3,10,5, + 0,178,183,3,74,37,0,179,183,3,86,43,0,180,183,3,108,54,0,181,183, + 3,32,16,0,182,175,1,0,0,0,182,176,1,0,0,0,182,177,1,0,0,0,182,178, + 1,0,0,0,182,179,1,0,0,0,182,180,1,0,0,0,182,181,1,0,0,0,183,9,1, + 0,0,0,184,185,5,8,0,0,185,186,5,11,0,0,186,187,3,126,63,0,187,188, + 5,98,0,0,188,195,1,0,0,0,189,190,5,8,0,0,190,191,5,13,0,0,191,192, + 3,126,63,0,192,193,5,98,0,0,193,195,1,0,0,0,194,184,1,0,0,0,194, + 189,1,0,0,0,195,11,1,0,0,0,196,197,5,9,0,0,197,198,5,10,0,0,198, + 199,3,126,63,0,199,200,5,48,0,0,200,201,3,128,64,0,201,202,5,98, + 0,0,202,13,1,0,0,0,203,204,5,9,0,0,204,205,5,11,0,0,205,206,3,126, + 63,0,206,207,5,42,0,0,207,208,3,128,64,0,208,209,5,98,0,0,209,15, + 1,0,0,0,210,215,3,10,5,0,211,215,3,12,6,0,212,215,3,14,7,0,213,215, + 3,32,16,0,214,210,1,0,0,0,214,211,1,0,0,0,214,212,1,0,0,0,214,213, + 1,0,0,0,215,17,1,0,0,0,216,217,5,10,0,0,217,218,3,126,63,0,218,219, + 5,48,0,0,219,222,3,128,64,0,220,221,5,49,0,0,221,223,3,128,64,0, + 222,220,1,0,0,0,222,223,1,0,0,0,223,224,1,0,0,0,224,225,5,98,0,0, + 225,19,1,0,0,0,226,228,3,16,8,0,227,226,1,0,0,0,228,231,1,0,0,0, + 229,227,1,0,0,0,229,230,1,0,0,0,230,232,1,0,0,0,231,229,1,0,0,0, + 232,233,5,11,0,0,233,235,3,126,63,0,234,236,3,22,11,0,235,234,1, + 0,0,0,235,236,1,0,0,0,236,237,1,0,0,0,237,238,5,48,0,0,238,239,3, + 128,64,0,239,240,5,42,0,0,240,250,3,128,64,0,241,242,5,53,0,0,242, + 247,3,26,13,0,243,244,5,99,0,0,244,246,3,26,13,0,245,243,1,0,0,0, + 246,249,1,0,0,0,247,245,1,0,0,0,247,248,1,0,0,0,248,251,1,0,0,0, + 249,247,1,0,0,0,250,241,1,0,0,0,250,251,1,0,0,0,251,252,1,0,0,0, + 252,256,5,101,0,0,253,255,3,34,17,0,254,253,1,0,0,0,255,258,1,0, + 0,0,256,254,1,0,0,0,256,257,1,0,0,0,257,259,1,0,0,0,258,256,1,0, + 0,0,259,260,5,102,0,0,260,21,1,0,0,0,261,262,5,107,0,0,262,267,3, + 24,12,0,263,264,5,99,0,0,264,266,3,24,12,0,265,263,1,0,0,0,266,269, + 1,0,0,0,267,265,1,0,0,0,267,268,1,0,0,0,268,270,1,0,0,0,269,267, + 1,0,0,0,270,271,5,108,0,0,271,23,1,0,0,0,272,273,5,14,0,0,273,276, + 3,126,63,0,274,275,5,97,0,0,275,277,5,4,0,0,276,274,1,0,0,0,276, + 277,1,0,0,0,277,292,1,0,0,0,278,279,5,3,0,0,279,289,3,126,63,0,280, + 281,5,5,0,0,281,286,3,26,13,0,282,283,5,109,0,0,283,285,3,26,13, + 0,284,282,1,0,0,0,285,288,1,0,0,0,286,284,1,0,0,0,286,287,1,0,0, + 0,287,290,1,0,0,0,288,286,1,0,0,0,289,280,1,0,0,0,289,290,1,0,0, + 0,290,292,1,0,0,0,291,272,1,0,0,0,291,278,1,0,0,0,292,25,1,0,0,0, + 293,295,3,126,63,0,294,296,3,28,14,0,295,294,1,0,0,0,295,296,1,0, + 0,0,296,27,1,0,0,0,297,298,5,107,0,0,298,303,3,30,15,0,299,300,5, + 99,0,0,300,302,3,30,15,0,301,299,1,0,0,0,302,305,1,0,0,0,303,301, + 1,0,0,0,303,304,1,0,0,0,304,306,1,0,0,0,305,303,1,0,0,0,306,307, + 5,108,0,0,307,29,1,0,0,0,308,309,5,10,0,0,309,316,3,126,63,0,310, + 311,5,11,0,0,311,316,3,26,13,0,312,313,5,3,0,0,313,316,3,126,63, + 0,314,316,3,110,55,0,315,308,1,0,0,0,315,310,1,0,0,0,315,312,1,0, + 0,0,315,314,1,0,0,0,316,31,1,0,0,0,317,318,5,2,0,0,318,320,3,126, + 63,0,319,321,3,22,11,0,320,319,1,0,0,0,320,321,1,0,0,0,321,322,1, + 0,0,0,322,323,5,110,0,0,323,324,3,110,55,0,324,325,5,98,0,0,325, + 33,1,0,0,0,326,330,3,38,19,0,327,330,3,42,21,0,328,330,3,36,18,0, + 329,326,1,0,0,0,329,327,1,0,0,0,329,328,1,0,0,0,330,35,1,0,0,0,331, + 332,5,16,0,0,332,333,3,126,63,0,333,334,5,48,0,0,334,335,3,128,64, + 0,335,336,5,97,0,0,336,337,3,110,55,0,337,338,5,96,0,0,338,339,3, + 110,55,0,339,340,5,101,0,0,340,341,5,65,0,0,341,342,5,48,0,0,342, + 343,3,128,64,0,343,344,5,98,0,0,344,345,5,102,0,0,345,37,1,0,0,0, + 346,347,5,14,0,0,347,348,3,126,63,0,348,349,5,48,0,0,349,350,3,128, + 64,0,350,351,5,97,0,0,351,352,3,110,55,0,352,356,5,101,0,0,353,355, + 3,40,20,0,354,353,1,0,0,0,355,358,1,0,0,0,356,354,1,0,0,0,356,357, + 1,0,0,0,357,359,1,0,0,0,358,356,1,0,0,0,359,360,5,102,0,0,360,39, + 1,0,0,0,361,362,5,55,0,0,362,363,5,48,0,0,363,364,3,128,64,0,364, + 365,5,98,0,0,365,381,1,0,0,0,366,367,5,56,0,0,367,368,5,48,0,0,368, + 369,3,128,64,0,369,370,5,98,0,0,370,381,1,0,0,0,371,372,5,57,0,0, + 372,373,5,58,0,0,373,374,5,48,0,0,374,375,3,128,64,0,375,376,5,59, + 0,0,376,377,5,48,0,0,377,378,3,128,64,0,378,379,5,98,0,0,379,381, + 1,0,0,0,380,361,1,0,0,0,380,366,1,0,0,0,380,371,1,0,0,0,381,41,1, + 0,0,0,382,383,5,15,0,0,383,384,3,126,63,0,384,385,5,48,0,0,385,386, + 3,128,64,0,386,387,5,97,0,0,387,388,3,116,58,0,388,390,3,46,23,0, + 389,391,5,76,0,0,390,389,1,0,0,0,390,391,1,0,0,0,391,392,1,0,0,0, + 392,396,5,101,0,0,393,395,3,44,22,0,394,393,1,0,0,0,395,398,1,0, + 0,0,396,394,1,0,0,0,396,397,1,0,0,0,397,399,1,0,0,0,398,396,1,0, + 0,0,399,400,5,102,0,0,400,43,1,0,0,0,401,402,5,62,0,0,402,403,5, + 48,0,0,403,404,3,128,64,0,404,405,5,98,0,0,405,426,1,0,0,0,406,407, + 5,63,0,0,407,408,5,48,0,0,408,409,3,128,64,0,409,410,5,98,0,0,410, + 426,1,0,0,0,411,412,5,64,0,0,412,413,5,48,0,0,413,414,3,128,64,0, + 414,415,5,98,0,0,415,426,1,0,0,0,416,417,5,57,0,0,417,418,5,58,0, + 0,418,419,5,48,0,0,419,420,3,128,64,0,420,421,5,59,0,0,421,422,5, + 48,0,0,422,423,3,128,64,0,423,424,5,98,0,0,424,426,1,0,0,0,425,401, + 1,0,0,0,425,406,1,0,0,0,425,411,1,0,0,0,425,416,1,0,0,0,426,45,1, + 0,0,0,427,428,5,10,0,0,428,437,3,126,63,0,429,430,5,11,0,0,430,432, + 3,126,63,0,431,433,3,28,14,0,432,431,1,0,0,0,432,433,1,0,0,0,433, + 437,1,0,0,0,434,435,5,3,0,0,435,437,3,126,63,0,436,427,1,0,0,0,436, + 429,1,0,0,0,436,434,1,0,0,0,437,47,1,0,0,0,438,440,3,16,8,0,439, + 438,1,0,0,0,440,443,1,0,0,0,441,439,1,0,0,0,441,442,1,0,0,0,442, + 444,1,0,0,0,443,441,1,0,0,0,444,445,5,13,0,0,445,446,3,126,63,0, + 446,447,5,48,0,0,447,448,3,128,64,0,448,449,5,42,0,0,449,452,3,128, + 64,0,450,451,5,43,0,0,451,453,5,111,0,0,452,450,1,0,0,0,452,453, + 1,0,0,0,453,454,1,0,0,0,454,458,5,101,0,0,455,457,3,50,25,0,456, + 455,1,0,0,0,457,460,1,0,0,0,458,456,1,0,0,0,458,459,1,0,0,0,459, + 461,1,0,0,0,460,458,1,0,0,0,461,462,5,102,0,0,462,49,1,0,0,0,463, + 467,3,52,26,0,464,467,3,54,27,0,465,467,3,56,28,0,466,463,1,0,0, + 0,466,464,1,0,0,0,466,465,1,0,0,0,467,51,1,0,0,0,468,469,5,16,0, + 0,469,471,3,126,63,0,470,472,3,22,11,0,471,470,1,0,0,0,471,472,1, + 0,0,0,472,473,1,0,0,0,473,474,5,48,0,0,474,475,3,128,64,0,475,476, + 5,97,0,0,476,477,3,110,55,0,477,478,5,96,0,0,478,479,3,110,55,0, + 479,480,5,50,0,0,480,482,3,60,30,0,481,483,3,58,29,0,482,481,1,0, + 0,0,482,483,1,0,0,0,483,484,1,0,0,0,484,485,5,52,0,0,485,487,3,62, + 31,0,486,488,3,66,33,0,487,486,1,0,0,0,487,488,1,0,0,0,488,489,1, + 0,0,0,489,490,5,98,0,0,490,53,1,0,0,0,491,492,5,17,0,0,492,494,3, + 126,63,0,493,495,3,22,11,0,494,493,1,0,0,0,494,495,1,0,0,0,495,496, + 1,0,0,0,496,497,5,48,0,0,497,498,3,128,64,0,498,499,5,97,0,0,499, + 500,3,110,55,0,500,501,5,96,0,0,501,503,3,110,55,0,502,504,3,66, + 33,0,503,502,1,0,0,0,503,504,1,0,0,0,504,505,1,0,0,0,505,506,5,98, + 0,0,506,55,1,0,0,0,507,508,5,18,0,0,508,509,3,126,63,0,509,510,5, + 48,0,0,510,511,3,128,64,0,511,512,5,19,0,0,512,513,3,126,63,0,513, + 514,5,97,0,0,514,516,3,110,55,0,515,517,3,66,33,0,516,515,1,0,0, + 0,516,517,1,0,0,0,517,518,1,0,0,0,518,519,5,98,0,0,519,57,1,0,0, + 0,520,521,5,51,0,0,521,522,3,110,55,0,522,59,1,0,0,0,523,524,7,0, + 0,0,524,61,1,0,0,0,525,544,5,54,0,0,526,527,5,10,0,0,527,544,3,126, + 63,0,528,529,5,3,0,0,529,544,3,126,63,0,530,531,5,12,0,0,531,540, + 5,103,0,0,532,537,3,26,13,0,533,534,5,99,0,0,534,536,3,26,13,0,535, + 533,1,0,0,0,536,539,1,0,0,0,537,535,1,0,0,0,537,538,1,0,0,0,538, + 541,1,0,0,0,539,537,1,0,0,0,540,532,1,0,0,0,540,541,1,0,0,0,541, + 542,1,0,0,0,542,544,5,104,0,0,543,525,1,0,0,0,543,526,1,0,0,0,543, + 528,1,0,0,0,543,530,1,0,0,0,544,63,1,0,0,0,545,550,3,126,63,0,546, + 547,5,99,0,0,547,549,3,126,63,0,548,546,1,0,0,0,549,552,1,0,0,0, + 550,548,1,0,0,0,550,551,1,0,0,0,551,65,1,0,0,0,552,550,1,0,0,0,553, + 554,5,53,0,0,554,558,5,101,0,0,555,557,3,68,34,0,556,555,1,0,0,0, + 557,560,1,0,0,0,558,556,1,0,0,0,558,559,1,0,0,0,559,561,1,0,0,0, + 560,558,1,0,0,0,561,562,5,102,0,0,562,67,1,0,0,0,563,564,5,27,0, + 0,564,565,3,126,63,0,565,566,5,48,0,0,566,567,3,128,64,0,567,568, + 5,97,0,0,568,569,3,110,55,0,569,570,3,70,35,0,570,571,5,98,0,0,571, + 606,1,0,0,0,572,573,5,28,0,0,573,574,3,126,63,0,574,575,5,48,0,0, + 575,576,3,128,64,0,576,577,5,97,0,0,577,578,3,116,58,0,578,579,3, + 46,23,0,579,580,3,70,35,0,580,581,5,98,0,0,581,606,1,0,0,0,582,583, + 5,11,0,0,583,584,3,126,63,0,584,585,5,48,0,0,585,586,3,128,64,0, + 586,587,5,97,0,0,587,589,3,126,63,0,588,590,3,28,14,0,589,588,1, + 0,0,0,589,590,1,0,0,0,590,591,1,0,0,0,591,592,5,98,0,0,592,606,1, + 0,0,0,593,594,5,18,0,0,594,595,3,126,63,0,595,596,5,48,0,0,596,597, + 3,128,64,0,597,598,5,97,0,0,598,601,3,126,63,0,599,600,5,20,0,0, + 600,602,3,110,55,0,601,599,1,0,0,0,601,602,1,0,0,0,602,603,1,0,0, + 0,603,604,5,98,0,0,604,606,1,0,0,0,605,563,1,0,0,0,605,572,1,0,0, + 0,605,582,1,0,0,0,605,593,1,0,0,0,606,69,1,0,0,0,607,608,5,103,0, + 0,608,613,3,72,36,0,609,610,5,99,0,0,610,612,3,72,36,0,611,609,1, + 0,0,0,612,615,1,0,0,0,613,611,1,0,0,0,613,614,1,0,0,0,614,616,1, + 0,0,0,615,613,1,0,0,0,616,617,5,104,0,0,617,71,1,0,0,0,618,619,7, + 1,0,0,619,73,1,0,0,0,620,621,5,26,0,0,621,622,3,76,38,0,622,75,1, + 0,0,0,623,626,3,78,39,0,624,626,3,82,41,0,625,623,1,0,0,0,625,624, + 1,0,0,0,626,77,1,0,0,0,627,628,5,27,0,0,628,629,3,126,63,0,629,630, + 5,48,0,0,630,631,3,128,64,0,631,632,5,36,0,0,632,633,3,126,63,0, + 633,634,5,97,0,0,634,635,3,110,55,0,635,636,5,37,0,0,636,639,3,80, + 40,0,637,638,5,38,0,0,638,640,3,118,59,0,639,637,1,0,0,0,639,640, + 1,0,0,0,640,641,1,0,0,0,641,642,5,98,0,0,642,79,1,0,0,0,643,650, + 5,70,0,0,644,645,5,71,0,0,645,646,5,105,0,0,646,647,3,110,55,0,647, + 648,5,106,0,0,648,650,1,0,0,0,649,643,1,0,0,0,649,644,1,0,0,0,650, + 81,1,0,0,0,651,652,5,28,0,0,652,653,3,126,63,0,653,654,5,48,0,0, + 654,655,3,128,64,0,655,656,5,101,0,0,656,657,3,84,42,0,657,658,3, + 84,42,0,658,659,5,102,0,0,659,83,1,0,0,0,660,661,3,46,23,0,661,662, + 5,29,0,0,662,663,3,126,63,0,663,664,5,48,0,0,664,665,3,128,64,0, + 665,667,3,116,58,0,666,668,5,76,0,0,667,666,1,0,0,0,667,668,1,0, + 0,0,668,671,1,0,0,0,669,670,5,44,0,0,670,672,3,128,64,0,671,669, + 1,0,0,0,671,672,1,0,0,0,672,674,1,0,0,0,673,675,5,45,0,0,674,673, + 1,0,0,0,674,675,1,0,0,0,675,678,1,0,0,0,676,677,5,46,0,0,677,679, + 3,128,64,0,678,676,1,0,0,0,678,679,1,0,0,0,679,681,1,0,0,0,680,682, + 5,47,0,0,681,680,1,0,0,0,681,682,1,0,0,0,682,683,1,0,0,0,683,684, + 5,98,0,0,684,85,1,0,0,0,685,686,5,21,0,0,686,687,3,126,63,0,687, + 688,5,22,0,0,688,690,3,126,63,0,689,691,3,28,14,0,690,689,1,0,0, + 0,690,691,1,0,0,0,691,694,1,0,0,0,692,693,5,48,0,0,693,695,3,128, + 64,0,694,692,1,0,0,0,694,695,1,0,0,0,695,698,1,0,0,0,696,697,5,43, + 0,0,697,699,5,111,0,0,698,696,1,0,0,0,698,699,1,0,0,0,699,700,1, + 0,0,0,700,704,5,101,0,0,701,703,3,88,44,0,702,701,1,0,0,0,703,706, + 1,0,0,0,704,702,1,0,0,0,704,705,1,0,0,0,705,707,1,0,0,0,706,704, + 1,0,0,0,707,708,5,102,0,0,708,87,1,0,0,0,709,710,5,25,0,0,710,714, + 3,76,38,0,711,714,3,92,46,0,712,714,3,90,45,0,713,709,1,0,0,0,713, + 711,1,0,0,0,713,712,1,0,0,0,714,89,1,0,0,0,715,716,5,33,0,0,716, + 717,3,126,63,0,717,718,5,34,0,0,718,719,5,35,0,0,719,720,5,31,0, + 0,720,721,5,18,0,0,721,722,3,126,63,0,722,723,5,32,0,0,723,724,5, + 28,0,0,724,725,3,126,63,0,725,726,5,100,0,0,726,727,3,126,63,0,727, + 728,5,98,0,0,728,91,1,0,0,0,729,730,5,23,0,0,730,731,3,94,47,0,731, + 732,5,24,0,0,732,733,3,98,49,0,733,734,5,98,0,0,734,93,1,0,0,0,735, + 736,3,126,63,0,736,737,5,100,0,0,737,738,3,96,48,0,738,95,1,0,0, + 0,739,751,3,126,63,0,740,751,5,65,0,0,741,751,5,55,0,0,742,751,5, + 56,0,0,743,751,5,62,0,0,744,751,5,63,0,0,745,751,5,64,0,0,746,751, + 5,66,0,0,747,751,5,67,0,0,748,751,5,68,0,0,749,751,5,69,0,0,750, + 739,1,0,0,0,750,740,1,0,0,0,750,741,1,0,0,0,750,742,1,0,0,0,750, + 743,1,0,0,0,750,744,1,0,0,0,750,745,1,0,0,0,750,746,1,0,0,0,750, + 747,1,0,0,0,750,748,1,0,0,0,750,749,1,0,0,0,751,97,1,0,0,0,752,753, + 5,27,0,0,753,754,3,126,63,0,754,755,5,100,0,0,755,756,3,100,50,0, + 756,775,1,0,0,0,757,758,5,28,0,0,758,759,3,126,63,0,759,760,5,100, + 0,0,760,761,3,126,63,0,761,762,5,100,0,0,762,763,3,102,51,0,763, + 775,1,0,0,0,764,765,5,13,0,0,765,766,3,126,63,0,766,767,5,100,0, + 0,767,769,3,126,63,0,768,770,3,28,14,0,769,768,1,0,0,0,769,770,1, + 0,0,0,770,772,1,0,0,0,771,773,3,104,52,0,772,771,1,0,0,0,772,773, + 1,0,0,0,773,775,1,0,0,0,774,752,1,0,0,0,774,757,1,0,0,0,774,764, + 1,0,0,0,775,99,1,0,0,0,776,777,7,2,0,0,777,101,1,0,0,0,778,779,7, + 3,0,0,779,103,1,0,0,0,780,781,5,30,0,0,781,785,5,101,0,0,782,784, + 3,106,53,0,783,782,1,0,0,0,784,787,1,0,0,0,785,783,1,0,0,0,785,786, + 1,0,0,0,786,788,1,0,0,0,787,785,1,0,0,0,788,789,5,102,0,0,789,105, + 1,0,0,0,790,791,3,126,63,0,791,792,5,24,0,0,792,793,5,27,0,0,793, + 800,3,126,63,0,794,795,5,32,0,0,795,796,5,28,0,0,796,797,3,126,63, + 0,797,798,5,100,0,0,798,799,3,126,63,0,799,801,1,0,0,0,800,794,1, + 0,0,0,800,801,1,0,0,0,801,802,1,0,0,0,802,803,5,98,0,0,803,844,1, + 0,0,0,804,805,3,126,63,0,805,806,5,24,0,0,806,807,5,28,0,0,807,808, + 3,126,63,0,808,809,5,100,0,0,809,816,3,126,63,0,810,811,5,32,0,0, + 811,812,5,28,0,0,812,813,3,126,63,0,813,814,5,100,0,0,814,815,3, + 126,63,0,815,817,1,0,0,0,816,810,1,0,0,0,816,817,1,0,0,0,817,818, + 1,0,0,0,818,819,5,98,0,0,819,844,1,0,0,0,820,821,3,126,63,0,821, + 822,5,24,0,0,822,823,5,11,0,0,823,825,3,126,63,0,824,826,3,28,14, + 0,825,824,1,0,0,0,825,826,1,0,0,0,826,833,1,0,0,0,827,828,5,32,0, + 0,828,829,5,28,0,0,829,830,3,126,63,0,830,831,5,100,0,0,831,832, + 3,126,63,0,832,834,1,0,0,0,833,827,1,0,0,0,833,834,1,0,0,0,834,835, + 1,0,0,0,835,836,5,98,0,0,836,844,1,0,0,0,837,838,3,126,63,0,838, + 839,5,24,0,0,839,840,5,18,0,0,840,841,3,126,63,0,841,842,5,98,0, + 0,842,844,1,0,0,0,843,790,1,0,0,0,843,804,1,0,0,0,843,820,1,0,0, + 0,843,837,1,0,0,0,844,107,1,0,0,0,845,846,5,18,0,0,846,847,3,126, + 63,0,847,848,5,24,0,0,848,849,3,126,63,0,849,850,5,100,0,0,850,852, + 3,126,63,0,851,853,3,104,52,0,852,851,1,0,0,0,852,853,1,0,0,0,853, + 854,1,0,0,0,854,855,5,98,0,0,855,109,1,0,0,0,856,903,3,114,57,0, + 857,903,5,77,0,0,858,903,5,78,0,0,859,860,5,79,0,0,860,903,3,128, + 64,0,861,862,5,80,0,0,862,863,5,107,0,0,863,864,3,126,63,0,864,865, + 5,108,0,0,865,903,1,0,0,0,866,867,5,81,0,0,867,868,5,107,0,0,868, + 870,3,126,63,0,869,871,3,28,14,0,870,869,1,0,0,0,870,871,1,0,0,0, + 871,872,1,0,0,0,872,873,5,108,0,0,873,903,1,0,0,0,874,875,5,6,0, + 0,875,876,5,107,0,0,876,877,3,126,63,0,877,878,5,108,0,0,878,903, + 1,0,0,0,879,880,5,82,0,0,880,881,5,107,0,0,881,882,3,110,55,0,882, + 883,5,108,0,0,883,903,1,0,0,0,884,885,5,83,0,0,885,886,5,107,0,0, + 886,887,3,110,55,0,887,888,5,108,0,0,888,903,1,0,0,0,889,890,5,84, + 0,0,890,894,5,101,0,0,891,893,3,112,56,0,892,891,1,0,0,0,893,896, + 1,0,0,0,894,892,1,0,0,0,894,895,1,0,0,0,895,897,1,0,0,0,896,894, + 1,0,0,0,897,903,5,102,0,0,898,900,3,126,63,0,899,901,3,28,14,0,900, + 899,1,0,0,0,900,901,1,0,0,0,901,903,1,0,0,0,902,856,1,0,0,0,902, + 857,1,0,0,0,902,858,1,0,0,0,902,859,1,0,0,0,902,861,1,0,0,0,902, + 866,1,0,0,0,902,874,1,0,0,0,902,879,1,0,0,0,902,884,1,0,0,0,902, + 889,1,0,0,0,902,898,1,0,0,0,903,111,1,0,0,0,904,905,3,126,63,0,905, + 906,5,97,0,0,906,907,3,110,55,0,907,908,5,98,0,0,908,113,1,0,0,0, + 909,910,7,4,0,0,910,115,1,0,0,0,911,912,7,5,0,0,912,117,1,0,0,0, + 913,922,3,128,64,0,914,922,5,111,0,0,915,922,5,112,0,0,916,922,5, + 93,0,0,917,922,5,94,0,0,918,922,5,95,0,0,919,922,3,120,60,0,920, + 922,3,124,62,0,921,913,1,0,0,0,921,914,1,0,0,0,921,915,1,0,0,0,921, + 916,1,0,0,0,921,917,1,0,0,0,921,918,1,0,0,0,921,919,1,0,0,0,921, + 920,1,0,0,0,922,119,1,0,0,0,923,932,5,101,0,0,924,929,3,122,61,0, + 925,926,5,99,0,0,926,928,3,122,61,0,927,925,1,0,0,0,928,931,1,0, + 0,0,929,927,1,0,0,0,929,930,1,0,0,0,930,933,1,0,0,0,931,929,1,0, + 0,0,932,924,1,0,0,0,932,933,1,0,0,0,933,934,1,0,0,0,934,935,5,102, + 0,0,935,121,1,0,0,0,936,937,3,128,64,0,937,938,5,97,0,0,938,939, + 3,118,59,0,939,123,1,0,0,0,940,949,5,103,0,0,941,946,3,118,59,0, + 942,943,5,99,0,0,943,945,3,118,59,0,944,942,1,0,0,0,945,948,1,0, + 0,0,946,944,1,0,0,0,946,947,1,0,0,0,947,950,1,0,0,0,948,946,1,0, + 0,0,949,941,1,0,0,0,949,950,1,0,0,0,950,951,1,0,0,0,951,952,5,104, + 0,0,952,125,1,0,0,0,953,954,7,6,0,0,954,127,1,0,0,0,955,956,5,114, + 0,0,956,129,1,0,0,0,81,142,149,170,182,194,214,222,229,235,247,250, + 256,267,276,286,289,291,295,303,315,320,329,356,380,390,396,425, + 432,436,441,452,458,466,471,482,487,494,503,516,537,540,543,550, + 558,589,601,605,613,625,639,649,667,671,674,678,681,690,694,698, + 704,713,750,769,772,774,785,800,816,825,833,843,852,870,894,900, + 902,921,929,932,946,949 ]; private static __ATN: antlr.ATN; @@ -3832,6 +4425,9 @@ export class WorkspaceItemContext extends antlr.ParserRuleContext { public constructorBindingDecl(): ConstructorBindingDeclContext | null { return this.getRuleContext(0, ConstructorBindingDeclContext); } + public typeAliasDecl(): TypeAliasDeclContext | null { + return this.getRuleContext(0, TypeAliasDeclContext); + } public override get ruleIndex(): number { return QuixosCapabilityParser.RULE_workspaceItem; } @@ -3960,6 +4556,9 @@ export class ResourcePreambleContext extends antlr.ParserRuleContext { public externalInterfaceDecl(): ExternalInterfaceDeclContext | null { return this.getRuleContext(0, ExternalInterfaceDeclContext); } + public typeAliasDecl(): TypeAliasDeclContext | null { + return this.getRuleContext(0, TypeAliasDeclContext); + } public override get ruleIndex(): number { return QuixosCapabilityParser.RULE_resourcePreamble; } @@ -4054,6 +4653,21 @@ export class InterfaceResourceDeclContext extends antlr.ParserRuleContext { return this.getRuleContext(i, ResourcePreambleContext); } + public typeParameters(): TypeParametersContext | null { + return this.getRuleContext(0, TypeParametersContext); + } + public REQUIRES(): antlr.TerminalNode | null { + return this.getToken(QuixosCapabilityParser.REQUIRES, 0); + } + public interfaceType(): InterfaceTypeContext[]; + public interfaceType(i: number): InterfaceTypeContext | null; + public interfaceType(i?: number): InterfaceTypeContext[] | InterfaceTypeContext | null { + if (i === undefined) { + return this.getRuleContexts(InterfaceTypeContext); + } + + return this.getRuleContext(i, InterfaceTypeContext); + } public interfaceMember(): InterfaceMemberContext[]; public interfaceMember(i: number): InterfaceMemberContext | null; public interfaceMember(i?: number): InterfaceMemberContext[] | InterfaceMemberContext | null { @@ -4063,6 +4677,15 @@ export class InterfaceResourceDeclContext extends antlr.ParserRuleContext { return this.getRuleContext(i, InterfaceMemberContext); } + public COMMA(): antlr.TerminalNode[]; + public COMMA(i: number): antlr.TerminalNode | null; + public COMMA(i?: number): antlr.TerminalNode | null | antlr.TerminalNode[] { + if (i === undefined) { + return this.getTokens(QuixosCapabilityParser.COMMA); + } else { + return this.getToken(QuixosCapabilityParser.COMMA, i); + } + } public override get ruleIndex(): number { return QuixosCapabilityParser.RULE_interfaceResourceDecl; } @@ -4076,6 +4699,234 @@ export class InterfaceResourceDeclContext extends antlr.ParserRuleContext { } +export class TypeParametersContext extends antlr.ParserRuleContext { + public constructor(parent: antlr.ParserRuleContext | null, invokingState: number) { + super(parent, invokingState); + } + public LT(): antlr.TerminalNode { + return this.getToken(QuixosCapabilityParser.LT, 0)!; + } + public typeParameter(): TypeParameterContext[]; + public typeParameter(i: number): TypeParameterContext | null; + public typeParameter(i?: number): TypeParameterContext[] | TypeParameterContext | null { + if (i === undefined) { + return this.getRuleContexts(TypeParameterContext); + } + + return this.getRuleContext(i, TypeParameterContext); + } + public GT(): antlr.TerminalNode { + return this.getToken(QuixosCapabilityParser.GT, 0)!; + } + public COMMA(): antlr.TerminalNode[]; + public COMMA(i: number): antlr.TerminalNode | null; + public COMMA(i?: number): antlr.TerminalNode | null | antlr.TerminalNode[] { + if (i === undefined) { + return this.getTokens(QuixosCapabilityParser.COMMA); + } else { + return this.getToken(QuixosCapabilityParser.COMMA, i); + } + } + public override get ruleIndex(): number { + return QuixosCapabilityParser.RULE_typeParameters; + } + public override accept(visitor: QuixosCapabilityVisitor): Result | null { + if (visitor.visitTypeParameters) { + return visitor.visitTypeParameters(this); + } else { + return visitor.visitChildren(this); + } + } +} + + +export class TypeParameterContext extends antlr.ParserRuleContext { + public constructor(parent: antlr.ParserRuleContext | null, invokingState: number) { + super(parent, invokingState); + } + public VALUE(): antlr.TerminalNode | null { + return this.getToken(QuixosCapabilityParser.VALUE, 0); + } + public identifier(): IdentifierContext { + return this.getRuleContext(0, IdentifierContext)!; + } + public COLON(): antlr.TerminalNode | null { + return this.getToken(QuixosCapabilityParser.COLON, 0); + } + public STORABLE(): antlr.TerminalNode | null { + return this.getToken(QuixosCapabilityParser.STORABLE, 0); + } + public OBJECT(): antlr.TerminalNode | null { + return this.getToken(QuixosCapabilityParser.OBJECT, 0); + } + public IMPLEMENTS(): antlr.TerminalNode | null { + return this.getToken(QuixosCapabilityParser.IMPLEMENTS, 0); + } + public interfaceType(): InterfaceTypeContext[]; + public interfaceType(i: number): InterfaceTypeContext | null; + public interfaceType(i?: number): InterfaceTypeContext[] | InterfaceTypeContext | null { + if (i === undefined) { + return this.getRuleContexts(InterfaceTypeContext); + } + + return this.getRuleContext(i, InterfaceTypeContext); + } + public AMP(): antlr.TerminalNode[]; + public AMP(i: number): antlr.TerminalNode | null; + public AMP(i?: number): antlr.TerminalNode | null | antlr.TerminalNode[] { + if (i === undefined) { + return this.getTokens(QuixosCapabilityParser.AMP); + } else { + return this.getToken(QuixosCapabilityParser.AMP, i); + } + } + public override get ruleIndex(): number { + return QuixosCapabilityParser.RULE_typeParameter; + } + public override accept(visitor: QuixosCapabilityVisitor): Result | null { + if (visitor.visitTypeParameter) { + return visitor.visitTypeParameter(this); + } else { + return visitor.visitChildren(this); + } + } +} + + +export class InterfaceTypeContext extends antlr.ParserRuleContext { + public constructor(parent: antlr.ParserRuleContext | null, invokingState: number) { + super(parent, invokingState); + } + public identifier(): IdentifierContext { + return this.getRuleContext(0, IdentifierContext)!; + } + public typeArguments(): TypeArgumentsContext | null { + return this.getRuleContext(0, TypeArgumentsContext); + } + public override get ruleIndex(): number { + return QuixosCapabilityParser.RULE_interfaceType; + } + public override accept(visitor: QuixosCapabilityVisitor): Result | null { + if (visitor.visitInterfaceType) { + return visitor.visitInterfaceType(this); + } else { + return visitor.visitChildren(this); + } + } +} + + +export class TypeArgumentsContext extends antlr.ParserRuleContext { + public constructor(parent: antlr.ParserRuleContext | null, invokingState: number) { + super(parent, invokingState); + } + public LT(): antlr.TerminalNode { + return this.getToken(QuixosCapabilityParser.LT, 0)!; + } + public typeArgument(): TypeArgumentContext[]; + public typeArgument(i: number): TypeArgumentContext | null; + public typeArgument(i?: number): TypeArgumentContext[] | TypeArgumentContext | null { + if (i === undefined) { + return this.getRuleContexts(TypeArgumentContext); + } + + return this.getRuleContext(i, TypeArgumentContext); + } + public GT(): antlr.TerminalNode { + return this.getToken(QuixosCapabilityParser.GT, 0)!; + } + public COMMA(): antlr.TerminalNode[]; + public COMMA(i: number): antlr.TerminalNode | null; + public COMMA(i?: number): antlr.TerminalNode | null | antlr.TerminalNode[] { + if (i === undefined) { + return this.getTokens(QuixosCapabilityParser.COMMA); + } else { + return this.getToken(QuixosCapabilityParser.COMMA, i); + } + } + public override get ruleIndex(): number { + return QuixosCapabilityParser.RULE_typeArguments; + } + public override accept(visitor: QuixosCapabilityVisitor): Result | null { + if (visitor.visitTypeArguments) { + return visitor.visitTypeArguments(this); + } else { + return visitor.visitChildren(this); + } + } +} + + +export class TypeArgumentContext extends antlr.ParserRuleContext { + public constructor(parent: antlr.ParserRuleContext | null, invokingState: number) { + super(parent, invokingState); + } + public ATOM(): antlr.TerminalNode | null { + return this.getToken(QuixosCapabilityParser.ATOM, 0); + } + public identifier(): IdentifierContext | null { + return this.getRuleContext(0, IdentifierContext); + } + public INTERFACE(): antlr.TerminalNode | null { + return this.getToken(QuixosCapabilityParser.INTERFACE, 0); + } + public interfaceType(): InterfaceTypeContext | null { + return this.getRuleContext(0, InterfaceTypeContext); + } + public OBJECT(): antlr.TerminalNode | null { + return this.getToken(QuixosCapabilityParser.OBJECT, 0); + } + public valueType(): ValueTypeContext | null { + return this.getRuleContext(0, ValueTypeContext); + } + public override get ruleIndex(): number { + return QuixosCapabilityParser.RULE_typeArgument; + } + public override accept(visitor: QuixosCapabilityVisitor): Result | null { + if (visitor.visitTypeArgument) { + return visitor.visitTypeArgument(this); + } else { + return visitor.visitChildren(this); + } + } +} + + +export class TypeAliasDeclContext extends antlr.ParserRuleContext { + public constructor(parent: antlr.ParserRuleContext | null, invokingState: number) { + super(parent, invokingState); + } + public TYPE(): antlr.TerminalNode { + return this.getToken(QuixosCapabilityParser.TYPE, 0)!; + } + public identifier(): IdentifierContext { + return this.getRuleContext(0, IdentifierContext)!; + } + public EQUAL(): antlr.TerminalNode { + return this.getToken(QuixosCapabilityParser.EQUAL, 0)!; + } + public valueType(): ValueTypeContext { + return this.getRuleContext(0, ValueTypeContext)!; + } + public SEMI(): antlr.TerminalNode { + return this.getToken(QuixosCapabilityParser.SEMI, 0)!; + } + public typeParameters(): TypeParametersContext | null { + return this.getRuleContext(0, TypeParametersContext); + } + public override get ruleIndex(): number { + return QuixosCapabilityParser.RULE_typeAliasDecl; + } + public override accept(visitor: QuixosCapabilityVisitor): Result | null { + if (visitor.visitTypeAliasDecl) { + return visitor.visitTypeAliasDecl(this); + } else { + return visitor.visitChildren(this); + } + } +} + + export class InterfaceMemberContext extends antlr.ParserRuleContext { public constructor(parent: antlr.ParserRuleContext | null, invokingState: number) { super(parent, invokingState); @@ -4398,6 +5249,12 @@ export class TargetConstraintContext extends antlr.ParserRuleContext { public INTERFACE(): antlr.TerminalNode | null { return this.getToken(QuixosCapabilityParser.INTERFACE, 0); } + public typeArguments(): TypeArgumentsContext | null { + return this.getRuleContext(0, TypeArgumentsContext); + } + public OBJECT(): antlr.TerminalNode | null { + return this.getToken(QuixosCapabilityParser.OBJECT, 0); + } public override get ruleIndex(): number { return QuixosCapabilityParser.RULE_targetConstraint; } @@ -4551,6 +5408,9 @@ export class PackageOperationExportContext extends antlr.ParserRuleContext { public SEMI(): antlr.TerminalNode { return this.getToken(QuixosCapabilityParser.SEMI, 0)!; } + public typeParameters(): TypeParametersContext | null { + return this.getRuleContext(0, TypeParametersContext); + } public eventClause(): EventClauseContext | null { return this.getRuleContext(0, EventClauseContext); } @@ -4604,6 +5464,9 @@ export class PackageFunctionExportContext extends antlr.ParserRuleContext { public SEMI(): antlr.TerminalNode { return this.getToken(QuixosCapabilityParser.SEMI, 0)!; } + public typeParameters(): TypeParametersContext | null { + return this.getRuleContext(0, TypeParametersContext); + } public dependencyBlock(): DependencyBlockContext | null { return this.getRuleContext(0, DependencyBlockContext); } @@ -4738,6 +5601,9 @@ export class ReceiverRequirementContext extends antlr.ParserRuleContext { public identifier(): IdentifierContext | null { return this.getRuleContext(0, IdentifierContext); } + public OBJECT(): antlr.TerminalNode | null { + return this.getToken(QuixosCapabilityParser.OBJECT, 0); + } public INTERFACES(): antlr.TerminalNode | null { return this.getToken(QuixosCapabilityParser.INTERFACES, 0); } @@ -4747,8 +5613,23 @@ export class ReceiverRequirementContext extends antlr.ParserRuleContext { public RBRACK(): antlr.TerminalNode | null { return this.getToken(QuixosCapabilityParser.RBRACK, 0); } - public identifierList(): IdentifierListContext | null { - return this.getRuleContext(0, IdentifierListContext); + public interfaceType(): InterfaceTypeContext[]; + public interfaceType(i: number): InterfaceTypeContext | null; + public interfaceType(i?: number): InterfaceTypeContext[] | InterfaceTypeContext | null { + if (i === undefined) { + return this.getRuleContexts(InterfaceTypeContext); + } + + return this.getRuleContext(i, InterfaceTypeContext); + } + public COMMA(): antlr.TerminalNode[]; + public COMMA(i: number): antlr.TerminalNode | null; + public COMMA(i?: number): antlr.TerminalNode | null | antlr.TerminalNode[] { + if (i === undefined) { + return this.getTokens(QuixosCapabilityParser.COMMA); + } else { + return this.getToken(QuixosCapabilityParser.COMMA, i); + } } public override get ruleIndex(): number { return QuixosCapabilityParser.RULE_receiverRequirement; @@ -4879,6 +5760,9 @@ export class DependencyPortContext extends antlr.ParserRuleContext { public INTERFACE(): antlr.TerminalNode | null { return this.getToken(QuixosCapabilityParser.INTERFACE, 0); } + public typeArguments(): TypeArgumentsContext | null { + return this.getRuleContext(0, TypeArgumentsContext); + } public CONSTRUCTOR(): antlr.TerminalNode | null { return this.getToken(QuixosCapabilityParser.CONSTRUCTOR, 0); } @@ -5242,6 +6126,9 @@ export class ConformanceDeclContext extends antlr.ParserRuleContext { public RBRACE(): antlr.TerminalNode { return this.getToken(QuixosCapabilityParser.RBRACE, 0)!; } + public typeArguments(): TypeArgumentsContext | null { + return this.getRuleContext(0, TypeArgumentsContext); + } public ID(): antlr.TerminalNode | null { return this.getToken(QuixosCapabilityParser.ID, 0); } @@ -5503,6 +6390,9 @@ export class OperationProviderContext extends antlr.ParserRuleContext { public PACKAGE(): antlr.TerminalNode | null { return this.getToken(QuixosCapabilityParser.PACKAGE, 0); } + public typeArguments(): TypeArgumentsContext | null { + return this.getRuleContext(0, TypeArgumentsContext); + } public dependencyBindingBlock(): DependencyBindingBlockContext | null { return this.getRuleContext(0, DependencyBindingBlockContext); } @@ -5661,6 +6551,9 @@ export class DependencyBindingContext extends antlr.ParserRuleContext { public INTERFACE(): antlr.TerminalNode | null { return this.getToken(QuixosCapabilityParser.INTERFACE, 0); } + public typeArguments(): TypeArgumentsContext | null { + return this.getRuleContext(0, TypeArgumentsContext); + } public CONSTRUCTOR(): antlr.TerminalNode | null { return this.getToken(QuixosCapabilityParser.CONSTRUCTOR, 0); } @@ -5752,6 +6645,12 @@ export class ValueTypeContext extends antlr.ParserRuleContext { public INTERFACE_REF(): antlr.TerminalNode | null { return this.getToken(QuixosCapabilityParser.INTERFACE_REF, 0); } + public typeArguments(): TypeArgumentsContext | null { + return this.getRuleContext(0, TypeArgumentsContext); + } + public REF(): antlr.TerminalNode | null { + return this.getToken(QuixosCapabilityParser.REF, 0); + } public OPTIONAL(): antlr.TerminalNode | null { return this.getToken(QuixosCapabilityParser.OPTIONAL, 0); } diff --git a/src/capability-language/generated/QuixosCapabilityVisitor.ts b/src/capability-language/generated/QuixosCapabilityVisitor.ts index e2b66ef..83fca76 100644 --- a/src/capability-language/generated/QuixosCapabilityVisitor.ts +++ b/src/capability-language/generated/QuixosCapabilityVisitor.ts @@ -13,6 +13,12 @@ import { ExternalInterfaceDeclContext } from "./QuixosCapabilityParser.js"; import { ResourcePreambleContext } from "./QuixosCapabilityParser.js"; import { AtomDeclContext } from "./QuixosCapabilityParser.js"; import { InterfaceResourceDeclContext } from "./QuixosCapabilityParser.js"; +import { TypeParametersContext } from "./QuixosCapabilityParser.js"; +import { TypeParameterContext } from "./QuixosCapabilityParser.js"; +import { InterfaceTypeContext } from "./QuixosCapabilityParser.js"; +import { TypeArgumentsContext } from "./QuixosCapabilityParser.js"; +import { TypeArgumentContext } from "./QuixosCapabilityParser.js"; +import { TypeAliasDeclContext } from "./QuixosCapabilityParser.js"; import { InterfaceMemberContext } from "./QuixosCapabilityParser.js"; import { OperationMemberContext } from "./QuixosCapabilityParser.js"; import { ValueMemberContext } from "./QuixosCapabilityParser.js"; @@ -137,6 +143,42 @@ export class QuixosCapabilityVisitor extends AbstractParseTreeVisitor Result; + /** + * Visit a parse tree produced by `QuixosCapabilityParser.typeParameters`. + * @param ctx the parse tree + * @return the visitor result + */ + visitTypeParameters?: (ctx: TypeParametersContext) => Result; + /** + * Visit a parse tree produced by `QuixosCapabilityParser.typeParameter`. + * @param ctx the parse tree + * @return the visitor result + */ + visitTypeParameter?: (ctx: TypeParameterContext) => Result; + /** + * Visit a parse tree produced by `QuixosCapabilityParser.interfaceType`. + * @param ctx the parse tree + * @return the visitor result + */ + visitInterfaceType?: (ctx: InterfaceTypeContext) => Result; + /** + * Visit a parse tree produced by `QuixosCapabilityParser.typeArguments`. + * @param ctx the parse tree + * @return the visitor result + */ + visitTypeArguments?: (ctx: TypeArgumentsContext) => Result; + /** + * Visit a parse tree produced by `QuixosCapabilityParser.typeArgument`. + * @param ctx the parse tree + * @return the visitor result + */ + visitTypeArgument?: (ctx: TypeArgumentContext) => Result; + /** + * Visit a parse tree produced by `QuixosCapabilityParser.typeAliasDecl`. + * @param ctx the parse tree + * @return the visitor result + */ + visitTypeAliasDecl?: (ctx: TypeAliasDeclContext) => Result; /** * Visit a parse tree produced by `QuixosCapabilityParser.interfaceMember`. * @param ctx the parse tree diff --git a/src/capability-language/generic-types.ts b/src/capability-language/generic-types.ts new file mode 100644 index 0000000..4a3f83b --- /dev/null +++ b/src/capability-language/generic-types.ts @@ -0,0 +1,392 @@ +import { + TypeSubstitution, + GenericTypeError, + instantiateInterface, + appliedInterfaceId, + valueType, + type AtomId, + type ClosedTypeArgument, + type GenericTypeEnvironment, + type InterfaceApplicationExpression, + type InterfaceRevision, + type ObjectTypeExpression, + type TypeArgumentExpression, + type TypeParameter, + type ValueAliasDefinition, + type ValueType, + type ValueTypeExpression, +} from "../capability-model/index.js"; +import type { + InterfaceTypeContext, + TargetConstraintContext, + TypeArgumentsContext, + TypeParametersContext, + TypeAliasDeclContext, + ValueTypeContext, +} from "./generated/QuixosCapabilityParser.js"; + +const literal = (context: { getText(): string }) => JSON.parse(context.getText()) as string; + +/** Lexical authoring scope; only `value`/`target` return installed types. */ +export class GenericSourceTypes { + readonly interfaces = new Map(); + readonly definitions = new Map(); + readonly applications = new Map(); + readonly aliases = new Map(); + parameters = new Map(); + self?: AtomId; + private active: string[] = []; + private applicationCount = 0; + + constructor(readonly atoms: Map) {} + + environment(): GenericTypeEnvironment { + return { + arguments: new Map(), + aliases: this.aliases, + self: this.self, + applyInterface: (id, args) => this.apply(id, args).revisionId, + // Obligations are retained on the application and discharged by workspace + // validation, where all atom conformances are known (not source order). + implementsInterface: () => true, + }; + } + + register(name: string, definition: InterfaceRevision) { + this.interfaces.set(name, definition); + this.definitions.set(definition.revisionId, definition); + } + + /** Check authored applications even when nobody has instantiated this template yet. */ + validateTemplate(definition: InterfaceRevision) { + const template = definition.template; + if (!template) return; + const aliases = new Map((template.aliases ?? []).map((alias) => [alias.id, alias])); + const parametersById = new Map( + [...template.parameters, ...(template.aliases ?? []).flatMap((alias) => alias.parameters)].map((parameter) => [ + parameter.id, + parameter, + ]), + ); + const replace = (node: unknown, arguments_: Map): unknown => { + if (!node || typeof node !== "object") return node; + if ("kind" in node && node.kind === "parameter" && "parameterId" in node) { + const arg = arguments_.get(String(node.parameterId)); + if (arg) return arg.kind === "value" ? arg.type : arg.target; + } + if (Array.isArray(node)) return node.map((entry) => replace(entry, arguments_)); + return Object.fromEntries(Object.entries(node).map(([key, value]) => [key, replace(value, arguments_)])); + }; + const implies = ( + actual: InterfaceApplicationExpression, + required: InterfaceApplicationExpression, + seen = new Set(), + ): boolean => { + const key = JSON.stringify(actual); + if (key === JSON.stringify(required)) return true; + if (seen.has(key) || seen.size > 128) return false; + seen.add(key); + const contract = this.definitions.get(actual.definitionId); + if (!contract?.template) + return (contract?.requiredInterfaces ?? []).includes(required.definitionId) && required.arguments.length === 0; + const args = new Map( + contract.template.parameters.map((parameter, index) => [parameter.id, actual.arguments[index]]), + ); + return contract.template.requires.some((parent) => + implies(replace(parent, args) as InterfaceApplicationExpression, required, seen), + ); + }; + const storable = (type: ValueTypeExpression, depth = 0): boolean => { + if (depth > 128) + throw new GenericTypeError( + "type-complexity-limit", + definition.displayName, + "Storable alias expansion exceeds depth limit", + ); + if (type.kind === "parameter") { + const parameter = parametersById.get(type.parameterId); + return parameter?.kind === "value" && Boolean(parameter.storable); + } + if (type.kind === "list" || type.kind === "optional") return storable(type.value, depth + 1); + if (type.kind === "alias") { + const alias = aliases.get(type.definitionId); + if (!alias || alias.parameters.length !== type.arguments.length) return false; + return storable( + replace( + alias.body, + new Map(alias.parameters.map((parameter, index) => [parameter.id, type.arguments[index]])), + ) as ValueTypeExpression, + depth + 1, + ); + } + return type.kind === "scalar" || (type.kind === "builtin" && type.name === "unit"); + }; + let remaining = 10000; + const visit = (node: unknown, path: string, depth = 0): void => { + if (depth > 128 || --remaining < 0) + throw new GenericTypeError("type-complexity-limit", path, "Type exceeds the depth or expansion budget"); + if (!node || typeof node !== "object") return; + if ("definitionId" in node && "arguments" in node) { + const application = node as InterfaceApplicationExpression | Extract; + const alias = "kind" in application && application.kind === "alias"; + const target = alias ? aliases.get(application.definitionId) : this.definitions.get(application.definitionId); + if (!target) + throw new GenericTypeError( + alias ? "unknown-alias" : "unknown-interface", + path, + `Unknown definition ${application.definitionId}`, + ); + if ("template" in target && target.template?.usesSelf) template.usesSelf = true; + const parameters = "parameters" in target ? target.parameters : (target.template?.parameters ?? []); + if (parameters.length !== application.arguments.length) + throw new GenericTypeError( + "type-arity", + path, + `Expected ${parameters.length} type arguments, received ${application.arguments.length}`, + ); + parameters.forEach((parameter, index) => { + if (parameter.kind !== application.arguments[index].kind) + throw new GenericTypeError( + "parameter-kind", + path, + `Expected ${parameter.kind}, received ${application.arguments[index].kind}`, + ); + const argument = application.arguments[index]; + if (parameter.kind === "value" && parameter.storable && argument.kind === "value" && !storable(argument.type)) + throw new GenericTypeError( + "non-storable-argument", + path, + "Generic application cannot prove its value argument is storable", + ); + if (parameter.kind === "object" && argument.kind === "object" && argument.target.kind === "parameter") { + const offered = parametersById.get(argument.target.parameterId); + const mapping = new Map(parameters.map((p, i) => [p.id, application.arguments[i]])); + for (const bound of parameter.implements) { + const required = replace(bound, mapping) as InterfaceApplicationExpression; + if (offered?.kind !== "object" || !offered.implements.some((evidence) => implies(evidence, required))) + throw new GenericTypeError( + "unsatisfied-bound", + path, + `Object parameter does not prove ${required.definitionId}`, + ); + } + } + }); + } + for (const [name, child] of Object.entries(node)) visit(child, `${path}.${name}`, depth + 1); + }; + visit(template, definition.displayName); + const active = new Set(); + const done = new Set(); + const checkAlias = (id: string) => { + if (done.has(id)) return; + if (active.has(id)) + throw new GenericTypeError("recursive-alias", id, `Recursive value alias: ${[...active, id].join(" -> ")}`); + active.add(id); + const walk = (node: unknown): void => { + if (!node || typeof node !== "object") return; + if ("kind" in node && node.kind === "alias") + checkAlias((node as ValueAliasDefinition["body"] & { definitionId: string }).definitionId); + Object.values(node).forEach(walk); + }; + walk(aliases.get(id)?.body); + active.delete(id); + done.add(id); + }; + for (const id of aliases.keys()) checkAlias(id); + } + + apply(id: string, arguments_: readonly ClosedTypeArgument[]): InterfaceRevision { + const definition = this.definitions.get(id); + if (!definition) throw new GenericTypeError("unknown-interface", id, "Unknown interface definition"); + const application = { + definitionId: definition.revisionId, + source: definition.source, + arguments: [...arguments_], + ...(definition.template?.usesSelf ? { self: this.self } : {}), + }; + const appliedId = definition.template ? appliedInterfaceId(application) : definition.revisionId; + const cached = this.applications.get(appliedId); + if (cached) return cached; + if (this.active.includes(id)) + throw new GenericTypeError( + "recursive-application", + id, + `Expanding recursive interface application: ${[...this.active, id].join(" -> ")}`, + ); + if (++this.applicationCount > 10000) + throw new GenericTypeError("type-complexity-limit", id, "Too many interface applications"); + this.active.push(id); + if (definition.template) + this.applications.set(appliedId, { + interfaceId: definition.interfaceId, + revisionId: appliedId, + displayName: definition.displayName, + source: definition.source, + members: [], + application, + }); + try { + const obligations: NonNullable = []; + const instance = instantiateInterface(definition, arguments_, { + ...this.environment(), + implementsInterface: (target, required) => { + obligations.push({ target, required }); + return true; + }, + }); + if (instance === definition) return definition; + instance.argumentRequirements = obligations; + this.applications.set(instance.revisionId, instance); + this.definitions.set(instance.revisionId, instance); + return instance; + } catch (error) { + this.applications.delete(appliedId); + throw error; + } finally { + this.active.pop(); + } + } + + interface(context: InterfaceTypeContext): InterfaceApplicationExpression { + return this.interfaceByName(context.identifier().getText(), context.typeArguments()); + } + + interfaceByName(name: string, arguments_: TypeArgumentsContext | null): InterfaceApplicationExpression { + const definition = this.interfaces.get(name); + if (!definition) throw new GenericTypeError("unknown-interface", name, "Unknown interface"); + return { definitionId: definition.revisionId, arguments: this.arguments(arguments_) }; + } + + arguments(context: TypeArgumentsContext | null): TypeArgumentExpression[] { + return (context?.typeArgument() ?? []).map((argument): TypeArgumentExpression => { + if (argument.INTERFACE()) + return { + kind: "object", + target: { kind: "application", application: this.interface(argument.interfaceType()!) }, + }; + if (argument.ATOM()) return { kind: "object", target: this.atom(argument.identifier()!.getText()) }; + if (argument.OBJECT()) return { kind: "object", target: this.objectParameter(argument.identifier()!.getText()) }; + const type = argument.valueType()!; + if (type.getText() === "Self") return { kind: "object", target: { kind: "self" } }; + const parameter = type.identifier() && this.parameters.get(type.identifier()!.getText()); + if (parameter?.kind === "object" && !type.typeArguments()) + return { kind: "object", target: { kind: "parameter", parameterId: parameter.id } }; + return { kind: "value", type: this.expression(type) }; + }); + } + + private atom(name: string): ObjectTypeExpression { + if (name === "Self") return { kind: "self" }; + const atomId = this.atoms.get(name); + if (!atomId) throw new GenericTypeError("unknown-atom", name, "Unknown atom"); + return { kind: "atom", atomId }; + } + + private objectParameter(name: string): ObjectTypeExpression { + if (name === "Self") return { kind: "self" }; + const parameter = this.parameters.get(name); + if (!parameter || parameter.kind !== "object") + throw new GenericTypeError("parameter-kind", name, "Expected an object parameter"); + return { kind: "parameter", parameterId: parameter.id }; + } + + targetExpression(context: TargetConstraintContext): ObjectTypeExpression { + const name = context.identifier().getText(); + if (context.ATOM()) return this.atom(name); + if (context.OBJECT()) return this.objectParameter(name); + return { kind: "application", application: this.interfaceByName(name, context.typeArguments()) }; + } + + expression(context: ValueTypeContext): ValueTypeExpression { + if (context.scalarType()) + return { + kind: "scalar", + name: context.scalarType()!.getText() as Extract["name"], + }; + if (context.UNIT()) return valueType.unit; + if (context.WATCH_HANDLE()) return valueType.watchHandle; + if (context.MESSAGE()) return valueType.message(literal(context.stringLiteral()!)); + if (context.OPTIONAL() || context.LIST()) + return { kind: context.LIST() ? "list" : "optional", value: this.expression(context.valueType()!) }; + if (context.RECORD()) { + const fields = context + .recordField() + .map((field) => [field.identifier().getText(), this.expression(field.valueType())] as const); + if (new Set(fields.map(([name]) => name)).size !== fields.length) + throw new GenericTypeError("duplicate-field", "record", "Duplicate record field"); + return { kind: "record", fields: Object.fromEntries(fields) }; + } + const name = context.identifier()!.getText(); + if (context.ATOM_REF()) return { kind: "object-ref", expectation: this.atom(name) }; + if (context.INTERFACE_REF()) + return { + kind: "object-ref", + expectation: { kind: "application", application: this.interfaceByName(name, context.typeArguments()) }, + }; + if (context.REF()) return { kind: "object-ref", expectation: this.objectParameter(name) }; + const parameter = this.parameters.get(name); + if (parameter) { + if (parameter.kind !== "value" || context.typeArguments()) + throw new GenericTypeError("parameter-kind", name, "Expected a value parameter; object parameters need ref"); + return { kind: "parameter", parameterId: parameter.id }; + } + if (!this.aliases.has(name)) throw new GenericTypeError("unknown-type", name, "Unknown value type"); + return { kind: "alias", definitionId: name, arguments: this.arguments(context.typeArguments()) }; + } + + value(context: ValueTypeContext): ValueType { + return new TypeSubstitution(this.environment()).value(this.expression(context)); + } + + target(context: TargetConstraintContext) { + return new TypeSubstitution(this.environment()).object(this.targetExpression(context)); + } + + declareParameters(context: TypeParametersContext | null, owner: string): TypeParameter[] { + const entries = context?.typeParameter() ?? []; + const result: TypeParameter[] = entries.map((parameter, index) => { + const name = parameter.identifier().getText(); + if (name === "Self" || this.parameters.has(name)) + throw new GenericTypeError("duplicate-parameter", owner, `Duplicate or reserved parameter ${name}`); + const declaration: TypeParameter = parameter.VALUE() + ? { + id: `${owner}/parameter/${index}`, + name, + kind: "value", + ...(parameter.STORABLE() ? { storable: true } : {}), + } + : { id: `${owner}/parameter/${index}`, name, kind: "object", implements: [] }; + this.parameters.set(name, declaration); + return declaration; + }); + entries.forEach((entry, index) => { + const parameter = result[index]; + if (parameter.kind === "object") + parameter.implements = entry.interfaceType().map((bound) => this.interface(bound)); + }); + return result; + } + + declareAliases(contexts: readonly TypeAliasDeclContext[]) { + for (const context of contexts) { + const name = context.identifier().getText(); + if (this.aliases.has(name)) throw new GenericTypeError("duplicate-alias", name, "Duplicate type alias"); + this.aliases.set(name, { id: name, parameters: [], body: valueType.unit }); + } + for (const context of contexts) { + const name = context.identifier().getText(); + const previous = this.parameters; + this.parameters = new Map(); + try { + this.aliases.set(name, { + id: name, + parameters: this.declareParameters(context.typeParameters(), JSON.stringify(["alias", name])), + body: this.expression(context.valueType()), + }); + } finally { + this.parameters = previous; + } + } + } +} diff --git a/src/capability-language/parser.ts b/src/capability-language/parser.ts index d928e0b..d8f1747 100644 --- a/src/capability-language/parser.ts +++ b/src/capability-language/parser.ts @@ -37,7 +37,15 @@ import { type StateSlotDefinition, type ValueType, type WorkspaceRevision, + GenericTypeError, + TypeSubstitution, + type ValueTypeExpression, + type ObjectTypeExpression, + type GenericPackageExport, + type GenericDependencyPort, + specializePackageExport, } from "../capability-model/index.js"; +import { GenericSourceTypes } from "./generic-types.js"; import { QuixosCapabilityLexer } from "./generated/QuixosCapabilityLexer.js"; import { QuixosCapabilityParser, @@ -69,6 +77,17 @@ import { export type CapabilityResourceKind = "interface" | "package"; +const genericLocations = new WeakMap(); +const atGenericSource = (context: ParserRuleContext, lower: () => T): T => { + try { + return lower(); + } catch (error) { + if (error instanceof GenericTypeError && !genericLocations.has(error)) + genericLocations.set(error, { line: context.start?.line ?? 0, column: context.start?.column ?? 0 }); + throw error; + } +}; + export type CapabilityResourceImport = { kind: CapabilityResourceKind; binding: string; @@ -98,6 +117,7 @@ export type CapabilityResource = externalAtoms: AtomDefinition[]; externalInterfaces: CapabilityExternalInterface[]; revision: InterfaceRevision; + specializations?: InterfaceRevision[]; } | { kind: "package"; @@ -105,6 +125,7 @@ export type CapabilityResource = externalAtoms: AtomDefinition[]; externalInterfaces: CapabilityExternalInterface[]; revision: PackageRevision; + specializations?: InterfaceRevision[]; }; export type CapabilityResourceCompileResult = @@ -137,6 +158,7 @@ export type CapabilitySourceCompileResult = }; interface InterfaceSymbol { + definition?: InterfaceRevision; revisionId: InterfaceRevision["revisionId"]; contractAvailable: boolean; members: Map< @@ -154,6 +176,7 @@ interface PackageExportSymbol { } interface PackageSymbol { + definition?: PackageRevision; revisionId: PackageRevision["revisionId"]; exports: Map; } @@ -164,6 +187,7 @@ interface AttachmentSymbol { } interface LoweringState { + types: GenericSourceTypes; fileName: string; diagnostics: CapabilitySourceDiagnostic[]; atoms: Map; @@ -263,6 +287,8 @@ const lowerConstraint = ( throw new Error("Missing target constraint after a successful parse"); } const name = identifier(context.identifier()); + if (context.OBJECT() || context.typeArguments() || name === "Self" || state.types.interfaces.get(name)?.template) + return state.types.target(context); if (context.ATOM()) { const atomId = requireSymbol(state, state.atoms, name, context, "atom"); return atomId ? { kind: "atom", atomId } : undefined; @@ -275,6 +301,16 @@ const lowerValueType = (state: LoweringState, context: ValueTypeContext | null): if (!context) { throw new Error("Missing value type after a successful parse"); } + if ( + context.REF() || + context.typeArguments() || + (context.identifier() && !context.ATOM_REF() && !context.INTERFACE_REF()) + ) + return state.types.value(context); + if (context.INTERFACE_REF() && state.types.interfaces.get(context.identifier()!.getText())?.template) + return state.types.value(context); + if ((context.ATOM_REF() || context.INTERFACE_REF()) && context.identifier()?.getText() === "Self") + return state.types.value(context); const scalar = context.scalarType(); if (scalar) { return { kind: "scalar", name: text(scalar) as never }; @@ -559,6 +595,188 @@ const lowerInterface = ( displayName: alias, source, members, + ...(context.interfaceType().length + ? { + requiredInterfaces: context + .interfaceType() + .map((entry) => new TypeSubstitution(state.types.environment()).application(state.types.interface(entry))), + } + : {}), + }; +}; + +const hasSelfIdentifier = (context: ParserRuleContext, state: LoweringState): boolean => { + if (context.ruleIndex === QuixosCapabilityParser.RULE_identifier && context.getText() === "Self") return true; + if (context.ruleIndex === QuixosCapabilityParser.RULE_valueType) { + const aliasName = context.children + .find((child) => "ruleIndex" in child && child.ruleIndex === QuixosCapabilityParser.RULE_identifier) + ?.getText(); + const seen = new Set(); + const containsSelf = (value: unknown): boolean => { + if (!value || typeof value !== "object") return false; + if ("kind" in value && value.kind === "self") return true; + if ("kind" in value && value.kind === "alias" && "definitionId" in value) { + const id = String(value.definitionId); + if (!seen.has(id)) { + seen.add(id); + if (containsSelf(state.types.aliases.get(id)?.body)) return true; + } + } + return Object.values(value).some(containsSelf); + }; + if (aliasName && containsSelf(state.types.aliases.get(aliasName)?.body)) return true; + } + if ( + context.ruleIndex === QuixosCapabilityParser.RULE_interfaceType || + ((context.ruleIndex === QuixosCapabilityParser.RULE_valueType || + context.ruleIndex === QuixosCapabilityParser.RULE_targetConstraint) && + ["interface", "interface-ref"].includes(context.getChild(0)?.getText() ?? "")) + ) { + const name = context.children + .find((child) => "ruleIndex" in child && child.ruleIndex === QuixosCapabilityParser.RULE_identifier) + ?.getText(); + if (name && state.types.interfaces.get(name)?.template?.usesSelf) return true; + } + return context.children.some((child) => "ruleIndex" in child && hasSelfIdentifier(child as ParserRuleContext, state)); +}; + +const lowerInterfaceTemplate = ( + state: LoweringState, + context: InterfaceResourceDeclContext, + source: SourceRevision, +): InterfaceRevision => { + const types = state.types; + const revisionId = capabilityId.interfaceRevision(stringValue(context.stringLiteral(1))); + const parameters = types.declareParameters(context.typeParameters(), JSON.stringify(["interface", revisionId])); + const signature = ( + name: string, + id: InterfaceOperation["id"], + input: ValueTypeExpression, + output: ValueTypeExpression, + mode: InterfaceOperationMode = "call", + eventType?: ValueTypeExpression, + ): InterfaceOperation => ({ + id, + displayName: name, + inputType: input, + outputType: output, + mode, + ...(eventType ? { eventType } : {}), + }); + const members: InterfaceMember[] = context + .interfaceMember() + .map((entry) => { + const value = entry.valueMember(); + if (value) { + const type = types.expression(value.valueType()); + return { + kind: "value", + id: capabilityId.member(stringValue(value.stringLiteral())), + displayName: identifier(value.identifier()), + valueType: type, + operations: value.valueMemberOperation().flatMap((operation) => { + const id = capabilityId.operation(stringValue(operation.stringLiteral(0))); + if (operation.GET()) return [signature("get", id, valueType.unit, type)]; + if (operation.SET()) return [signature("set", id, type, valueType.unit)]; + return [ + signature("watch-start", id, valueType.unit, valueType.watchHandle, "watch-start", type), + signature( + "watch-stop", + capabilityId.operation(stringValue(operation.stringLiteral(1))), + valueType.watchHandle, + valueType.unit, + "watch-stop", + ), + ]; + }), + }; + } + const operation = entry.operationMember(); + if (operation) { + const inputType = types.expression(operation.valueType(0)!); + const outputType = types.expression(operation.valueType(1)!); + return { + kind: "operation", + id: capabilityId.member(stringValue(operation.stringLiteral(0))), + displayName: identifier(operation.identifier()), + inputType, + outputType, + operations: [ + signature("call", capabilityId.operation(stringValue(operation.stringLiteral(1))), inputType, outputType), + ], + }; + } + const relationship = entry.relationshipMember()!; + const target = types.targetExpression(relationship.targetConstraint()); + const targetType: ValueTypeExpression = { kind: "object-ref", expectation: target }; + const cardinality = lowerCardinality(relationship.cardinality()); + const resolved: ValueTypeExpression = + cardinality === "exactly-one" + ? targetType + : { kind: cardinality === "optional-one" ? "optional" : "list", value: targetType }; + return { + kind: "relationship", + id: capabilityId.member(stringValue(relationship.stringLiteral())), + displayName: identifier(relationship.identifier()), + target, + cardinality, + ordered: Boolean(relationship.ORDERED()), + operations: relationship.relationshipOperation().flatMap((operation) => { + const id = capabilityId.operation(stringValue(operation.stringLiteral(0))); + if (operation.RESOLVE()) return [signature("resolve", id, valueType.unit, resolved)]; + if (operation.CONNECT() || operation.DISCONNECT()) + return [signature(operation.CONNECT() ? "connect" : "disconnect", id, targetType, valueType.unit)]; + return [ + signature("watch-start", id, valueType.unit, valueType.watchHandle, "watch-start", resolved), + signature( + "watch-stop", + capabilityId.operation(stringValue(operation.stringLiteral(1))), + valueType.watchHandle, + valueType.unit, + "watch-stop", + ), + ]; + }), + }; + }); + const requires = context.interfaceType().map((requirement) => types.interface(requirement)); + const memberIds = new Set(); + const memberNames = new Set(); + const operationIds = new Set(); + for (const member of members) { + if (!member.id || memberIds.has(member.id) || memberNames.has(member.displayName)) + throw new GenericTypeError( + "duplicate-interface-member", + revisionId, + `Invalid or duplicate member ${member.displayName}`, + ); + memberIds.add(member.id); + memberNames.add(member.displayName); + const operationNames = new Set(); + for (const operation of member.operations) { + if (!operation.id || operationIds.has(operation.id) || operationNames.has(operation.displayName)) + throw new GenericTypeError( + "duplicate-interface-operation", + revisionId, + `Invalid or duplicate operation ${member.displayName}.${operation.displayName}`, + ); + operationIds.add(operation.id); + operationNames.add(operation.displayName); + } + } + const hasSelf = (value: unknown): boolean => + value !== null && + typeof value === "object" && + (("kind" in value && value.kind === "self") || Object.values(value).some(hasSelf)); + const aliases = [...types.aliases.values()]; + types.parameters = new Map(); + return { + interfaceId: capabilityId.interface(stringValue(context.stringLiteral(0))), + revisionId, + displayName: identifier(context.identifier()), + source, + members: [], + template: { parameters, members, requires, aliases, usesSelf: hasSelf([parameters, members, requires, aliases]) }, }; }; @@ -612,7 +830,12 @@ const lowerDependencyPort = (state: LoweringState, context: DependencyPortContex } const targetName = identifier(context.identifier(1)); if (context.INTERFACE()) { - const target = requireSymbol(state, state.interfaces, targetName, context, "interface"); + let target = requireSymbol(state, state.interfaces, targetName, context, "interface"); + if (target && (context.typeArguments() || target.definition?.template)) { + const expression = state.types.interfaceByName(targetName, context.typeArguments()); + const id = new TypeSubstitution(state.types.environment()).application(expression); + target = interfaceSymbolFor(state.types.definitions.get(id)!); + } if (target && !target.contractAvailable) { loweringIssue( state, @@ -665,6 +888,12 @@ const lowerReceiver = ( state: LoweringState, context: PackageOperationExportContext["receiverRequirement"] extends () => infer Result ? Result : never, ): PackageReceiverRequirement => { + if (context.OBJECT()) + throw new GenericTypeError( + "unbound-parameter", + context.getText(), + "An object-parameter receiver requires a generic operation declaration", + ); if (context.ANY()) { return { kind: "any-object" }; } @@ -675,11 +904,12 @@ const lowerReceiver = ( atomId: requireSymbol(state, state.atoms, name, context, "atom") ?? capabilityId.atom(`unresolved:${name}`), }; } - const list = context.identifierList(); return { kind: "all-interfaces", - interfaceRevisionIds: (list?.identifier() ?? []).map((entry) => { - const name = identifier(entry); + interfaceRevisionIds: context.interfaceType().map((entry) => { + const name = identifier(entry.identifier()); + if (entry.typeArguments() || state.types.interfaces.get(name)?.template) + return new TypeSubstitution(state.types.environment()).application(state.types.interface(entry)); return ( requireSymbol(state, state.interfaces, name, entry, "interface")?.revisionId ?? capabilityId.interfaceRevision(`unresolved:${name}`) @@ -773,22 +1003,148 @@ const lowerPackageConstructor = ( return entry; }; +const lowerGenericPackageExport = ( + state: LoweringState, + context: PackageOperationExportContext | PackageFunctionExportContext, +): GenericPackageExport => { + const previous = state.types.parameters; + state.types.parameters = new Map(); + const id = capabilityId.packageExport(stringValue(context.stringLiteral())); + try { + const parameters = state.types.declareParameters(context.typeParameters(), JSON.stringify(["export", id])); + const dependencyPorts = (context.dependencyBlock()?.dependencyPort() ?? []).map((port): GenericDependencyPort => { + const base = { + id: capabilityId.dependencyPort(stringValue(port.stringLiteral())), + displayName: identifier(port.identifier(0)), + }; + const primitives = + port + .primitiveList() + ?.primitive() + .map((item) => text(item)) ?? []; + if (port.STATE()) + return { + ...base, + requirement: { + kind: "state", + valueType: state.types.expression(port.valueType()!), + primitives: primitives as StatePrimitive[], + }, + }; + if (port.EDGE()) + return { + ...base, + requirement: { + kind: "edge", + target: state.types.targetExpression(port.targetConstraint()!), + cardinality: lowerCardinality(port.cardinality()!), + primitives: primitives as EdgePrimitive[], + }, + }; + if (port.INTERFACE()) + return { + ...base, + requirement: { + kind: "interface", + application: state.types.interfaceByName(identifier(port.identifier(1)), port.typeArguments()), + }, + }; + const name = identifier(port.identifier(1)), + parameter = state.types.parameters.get(name); + const target: ObjectTypeExpression = + parameter?.kind === "object" + ? { kind: "parameter", parameterId: parameter.id } + : { kind: "atom", atomId: requireSymbol(state, state.atoms, name, port, "atom")! }; + if (!port.valueType()) + throw new GenericTypeError("missing-constructor-input", id, "Constructor ports require an explicit input type"); + return { + ...base, + requirement: { kind: "constructor", target, inputType: state.types.expression(port.valueType()!) }, + }; + }); + const operation = "receiverRequirement" in context ? context : undefined; + const receiver = operation?.receiverRequirement(); + let receiverRequirement: GenericPackageExport["receiverRequirement"] = { kind: "any-object" }; + if (receiver?.ATOM()) + receiverRequirement = { + kind: "target", + target: { + kind: "atom", + atomId: requireSymbol(state, state.atoms, identifier(receiver.identifier()), receiver, "atom")!, + }, + }; + if (receiver?.OBJECT()) { + const name = identifier(receiver.identifier()); + const parameter = state.types.parameters.get(name); + if (parameter?.kind !== "object") + throw new GenericTypeError("parameter-kind", id, "Receiver needs an object parameter"); + receiverRequirement = { kind: "target", target: { kind: "parameter", parameterId: parameter.id } }; + } + if (receiver?.INTERFACES()) + receiverRequirement = { + kind: "interfaces", + interfaces: receiver.interfaceType().map((item) => state.types.interface(item)), + }; + const definition: GenericPackageExport = { + id, + displayName: identifier(context.identifier()), + parameters, + kind: operation ? "operation" : "function", + inputType: state.types.expression(context.valueType(0)!), + outputType: state.types.expression(context.valueType(1)!), + dependencyPorts, + receiverRequirement, + ...(operation ? { mode: text(operation.operationMode()) as InterfaceOperationMode } : {}), + ...(operation?.eventClause() ? { eventType: state.types.expression(operation.eventClause()!.valueType()) } : {}), + aliases: [...state.types.aliases.values()], + }; + const bindSelf = (value: unknown): unknown => { + if (!value || typeof value !== "object") return value; + if ("kind" in value && value.kind === "self") { + if (receiverRequirement.kind !== "target") + throw new GenericTypeError("unbound-self", id, "Generic Self requires an exact or object-parameter receiver"); + return receiverRequirement.target; + } + if (Array.isArray(value)) return value.map(bindSelf); + return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, bindSelf(child)])); + }; + return bindSelf(definition) as GenericPackageExport; + } finally { + state.types.parameters = previous; + } +}; + const lowerPackage = ( state: LoweringState, context: PackageResourceDeclContext, source: SourceRevision, ): PackageRevision => { const alias = identifier(context.identifier()); - const exports = context.packageExport().map((exportContext) => { + const genericExports: GenericPackageExport[] = []; + const exports = context.packageExport().flatMap((exportContext): PackageExport[] => { const operation = exportContext.packageOperationExport(); - if (operation) { - return lowerPackageOperation(state, alias, operation); + const generic = operation ?? exportContext.packageFunctionExport(); + if (generic?.typeParameters()) { + genericExports.push(atGenericSource(generic, () => lowerGenericPackageExport(state, generic))); + return []; } - const fn = exportContext.packageFunctionExport(); - if (fn) { - return lowerPackageFunction(state, alias, fn); + const constructor = exportContext.packageConstructorExport(); + const receiver = operation?.receiverRequirement(); + const selfName = constructor + ? identifier(constructor.identifier(1)) + : receiver?.ATOM() + ? identifier(receiver.identifier()) + : undefined; + const previousSelf = state.types.self; + state.types.self = selfName ? state.atoms.get(selfName) : undefined; + try { + if (operation) return [lowerPackageOperation(state, alias, operation)]; + const fn = exportContext.packageFunctionExport(); + if (fn) return [lowerPackageFunction(state, alias, fn)]; + return [lowerPackageConstructor(state, alias, constructor!)]; + } finally { + state.types.self = previousSelf; } - return lowerPackageConstructor(state, alias, exportContext.packageConstructorExport()!); }); return { packageId: capabilityId.package(stringValue(context.stringLiteral(0))), @@ -797,6 +1153,7 @@ const lowerPackage = ( displayName: alias, source, exports, + ...(genericExports.length ? { genericExports } : {}), }; }; @@ -978,7 +1335,13 @@ const lowerBoundDependencies = ( } if (entry.INTERFACE()) { const interfaceName = identifier(entry.identifier(1)); - const interfaceSymbol = requireSymbol(state, state.interfaces, interfaceName, entry, "interface"); + let interfaceSymbol = requireSymbol(state, state.interfaces, interfaceName, entry, "interface"); + if (interfaceSymbol && (entry.typeArguments() || interfaceSymbol.definition?.template)) { + const id = new TypeSubstitution(state.types.environment()).application( + state.types.interfaceByName(interfaceName, entry.typeArguments()), + ); + interfaceSymbol = interfaceSymbolFor(state.types.definitions.get(id)!); + } const via = entry.VIA() ? traversal(identifier(entry.identifier(2)), identifier(entry.identifier(3))) : undefined; if (entry.VIA() && !via) return []; return interfaceSymbol @@ -1047,10 +1410,23 @@ const lowerConformance = ( const atomName = identifier(context.identifier(0)); const interfaceName = identifier(context.identifier(1)); const atomId = requireSymbol(state, state.atoms, atomName, context, "atom"); - const interfaceSymbol = requireSymbol(state, state.interfaces, interfaceName, context, "interface"); + let interfaceSymbol = requireSymbol(state, state.interfaces, interfaceName, context, "interface"); if (!atomId || !interfaceSymbol) { return undefined; } + const originalSymbol = interfaceSymbol; + if (context.typeArguments() || interfaceSymbol.definition?.template) { + const previousSelf = state.types.self; + state.types.self = atomId; + try { + const expression = state.types.interfaceByName(interfaceName, context.typeArguments()); + const id = new TypeSubstitution(state.types.environment()).application(expression); + interfaceSymbol = interfaceSymbolFor(state.types.definitions.get(id)!); + state.interfaces.set(interfaceName, interfaceSymbol); + } finally { + state.types.self = previousSelf; + } + } const operationBindings = context .conformanceItem() .flatMap((item) => { @@ -1119,9 +1495,30 @@ const lowerConformance = ( const packageName = identifier(provider.identifier(0)); const exportName = identifier(provider.identifier(1)); const packageSymbol = requireSymbol(state, state.packages, packageName, provider, "package"); - const exportSymbol = packageSymbol + let exportSymbol = packageSymbol ? requireSymbol(state, packageSymbol.exports, exportName, provider, `export on ${packageName}`) : undefined; + const generic = packageSymbol?.definition?.genericExports?.find((entry) => entry.displayName === exportName); + if (packageSymbol?.definition && generic) { + const arguments_ = state.types + .arguments(provider.typeArguments()) + .map((entry) => new TypeSubstitution({ ...state.types.environment(), self: atomId }).argument(entry)); + const pkg = packageSymbol.definition; + const specialized = specializePackageExport(pkg, generic, arguments_, { + ...state.types.environment(), + implementsInterface: (target, required) => { + (pkg.argumentRequirements ??= []).push({ target, required }); + return true; + }, + }); + if (!pkg.exports.some((entry) => entry.id === specialized.id)) pkg.exports.push(specialized); + exportSymbol = { + exportId: specialized.id, + ports: new Map(specialized.dependencyPorts.map((entry) => [entry.displayName, entry.id])), + }; + } else if (provider.typeArguments()) { + throw new GenericTypeError("type-arity", exportName, "Non-generic export takes no type arguments"); + } return packageSymbol && exportSymbol ? [ { @@ -1146,6 +1543,7 @@ const lowerConformance = ( const lowered = lowerRelationshipMaterialization(state, interfaceName, materialization); return lowered ? [lowered] : []; }); + state.interfaces.set(interfaceName, originalSymbol); return { atomId, interfaceRevisionId: interfaceSymbol.revisionId, @@ -1158,6 +1556,7 @@ const lowerConformance = ( }; const interfaceSymbolFor = (revision: InterfaceRevision): InterfaceSymbol => ({ + definition: revision, revisionId: revision.revisionId, contractAvailable: true, members: new Map( @@ -1172,9 +1571,10 @@ const interfaceSymbolFor = (revision: InterfaceRevision): InterfaceSymbol => ({ }); const packageSymbolFor = (revision: PackageRevision): PackageSymbol => ({ + definition: structuredClone(revision), revisionId: revision.revisionId, exports: new Map( - revision.exports.map((entry) => [ + [...revision.exports, ...(revision.genericExports ?? [])].map((entry) => [ entry.displayName, { exportId: entry.id, @@ -1193,8 +1593,10 @@ const registerImports = ( state: LoweringState, contexts: readonly ResourceImportDeclContext[], environment: CapabilityImportEnvironment, -) => - contexts.map((context) => { +) => { + for (const definition of environment.interfaceClosure ?? []) + state.types.definitions.set(definition.revisionId, definition); + return contexts.map((context) => { const imported = resourceImport(context); if (imported.kind === "interface") { const revision = environment.interfaces?.get(imported.binding); @@ -1207,6 +1609,7 @@ const registerImports = ( ); } else { declareSymbol(state, state.interfaces, imported.binding, interfaceSymbolFor(revision), context, "interface"); + state.types.register(imported.binding, revision); } } else { const revision = environment.packages?.get(imported.binding); @@ -1223,6 +1626,7 @@ const registerImports = ( } return imported; }); +}; const uniqueExactRevisions = < Revision extends { @@ -1295,6 +1699,8 @@ const lowerWorkspace = ( : []; }); + state.types.declareAliases(items.flatMap((item) => item.typeAliasDecl() ?? [])); + const interfaceImports = uniqueExactRevisions([ ...(environment.interfaceClosure ?? []), ...[...(environment.interfaces?.values() ?? [])], @@ -1319,6 +1725,7 @@ const lowerWorkspace = ( } const conformance = item.conformanceDecl(); if (conformance) { + state.types.self = state.atoms.get(identifier(conformance.identifier(0))); const attachments = conformance.conformanceItem().flatMap((entry) => { const declaration = entry.attachmentDecl(); if (!declaration) { @@ -1332,6 +1739,7 @@ const lowerWorkspace = ( return [attachment]; }); privateAttachments.set(conformance, attachments); + state.types.self = undefined; } } @@ -1340,7 +1748,9 @@ const lowerWorkspace = ( if (!context) { return []; } - const conformance = lowerConformance(state, context, privateAttachments.get(context) ?? []); + const conformance = atGenericSource(context, () => + lowerConformance(state, context, privateAttachments.get(context) ?? []), + ); return conformance ? [conformance] : []; }); @@ -1382,8 +1792,15 @@ const lowerWorkspace = ( sourceRootCommit: stringValue(context.stringLiteral(2)), atoms, sharedAttachments, - interfaceImports, - packageImports, + interfaceImports: uniqueExactRevisions([ + ...interfaceImports.filter((entry) => !entry.template), + ...state.types.applications.values(), + ]), + packageImports: packageImports.map( + (revision) => + [...state.packages.values()].find((entry) => entry.revisionId === revision.revisionId)?.definition ?? + revision, + ), conformances, constructors, }, @@ -1408,14 +1825,18 @@ export const parseDocument = ( return { tree, tokens, diagnostics }; }; -const newLoweringState = (fileName: string, diagnostics: CapabilitySourceDiagnostic[]): LoweringState => ({ - fileName, - diagnostics, - atoms: new Map(), - interfaces: new Map(), - packages: new Map(), - attachments: new Map(), -}); +const newLoweringState = (fileName: string, diagnostics: CapabilitySourceDiagnostic[]): LoweringState => { + const atoms = new Map(); + return { + fileName, + diagnostics, + atoms, + types: new GenericSourceTypes(atoms), + interfaces: new Map(), + packages: new Map(), + attachments: new Map(), + }; +}; const validationDiagnostics = ( fileName: string, @@ -1471,6 +1892,7 @@ const resourcePreambleParts = (contexts: readonly ResourcePreambleContext[]) => imports: contexts.flatMap((context) => context.resourceImportDecl() ?? []), atoms: contexts.flatMap((context) => context.externalAtomDecl() ?? []), interfaces: contexts.flatMap((context) => context.externalInterfaceDecl() ?? []), + aliases: contexts.flatMap((context) => context.typeAliasDecl() ?? []), }); const resourceValidationWorkspace = ( @@ -1493,6 +1915,7 @@ const resourceValidationWorkspace = ( ...(environment.interfaceClosure ?? []), ...[...(environment.interfaces?.values() ?? [])], ...(resource.kind === "interface" ? [resource.revision] : []), + ...(resource.specializations ?? []), ...resource.externalInterfaces .filter((requirement) => !resolvedInterfaceIds.has(requirement.revisionId)) .map( @@ -1507,7 +1930,9 @@ const resourceValidationWorkspace = ( members: [], }), ), - ]), + ]) + .filter((entry) => !entry.template) + .map((entry) => ({ ...entry, argumentRequirements: [] })), packageImports: uniqueExactRevisions([ ...(environment.packageClosure ?? []), ...[...(environment.packages?.values() ?? [])], @@ -1518,7 +1943,7 @@ const resourceValidationWorkspace = ( }; }; -export const compileCapabilityResourceSource = ( +const compileCapabilityResourceSourceInternal = ( sourceText: string, options: { source: SourceRevision; @@ -1556,10 +1981,18 @@ export const compileCapabilityResourceSource = ( const externalAtoms = externalAtomsFrom(state, preamble.atoms); const imports = registerImports(state, preamble.imports, environment); const externalInterfaces = externalInterfacesFrom(state, preamble.interfaces); + state.types.declareAliases(preamble.aliases); let resource: CapabilityResource; if (interfaceContext) { const alias = identifier(interfaceContext.identifier()); + state.types.register(alias, { + interfaceId: capabilityId.interface(stringValue(interfaceContext.stringLiteral(0))), + revisionId: capabilityId.interfaceRevision(stringValue(interfaceContext.stringLiteral(1))), + displayName: alias, + source: options.source, + members: [], + }); declareSymbol( state, state.interfaces, @@ -1577,7 +2010,10 @@ export const compileCapabilityResourceSource = ( imports, externalAtoms, externalInterfaces, - revision: lowerInterface(state, interfaceContext, options.source), + revision: + interfaceContext.typeParameters() || hasSelfIdentifier(interfaceContext, state) + ? atGenericSource(interfaceContext, () => lowerInterfaceTemplate(state, interfaceContext, options.source)) + : lowerInterface(state, interfaceContext, options.source), }; } else { const context = packageContext!; @@ -1602,6 +2038,77 @@ export const compileCapabilityResourceSource = ( }; } + if (resource.kind === "interface") { + state.types.register(identifier(interfaceContext!.identifier()), resource.revision); + atGenericSource(interfaceContext!, () => state.types.validateTemplate(resource.revision)); + } else { + const names = new Set(resource.revision.exports.map((entry) => entry.displayName)); + const ids = new Set(resource.revision.exports.map((entry) => entry.id)); + for (const entry of resource.revision.genericExports ?? []) { + if (names.has(entry.displayName) || ids.has(entry.id)) + throw new GenericTypeError("duplicate-package-export", entry.displayName, "Duplicate generic export"); + names.add(entry.displayName); + ids.add(entry.id); + if ( + entry.kind === "operation" && + (entry.mode === "watch-start" || entry.mode === "subscribe") !== Boolean(entry.eventType) + ) + throw new GenericTypeError( + "invalid-operation", + entry.displayName, + "Only watch-start/subscribe operations must declare an event type", + ); + const portIds = new Set(), + portNames = new Set(); + const types: ValueTypeExpression[] = [ + entry.inputType, + entry.outputType, + ...(entry.eventType ? [entry.eventType] : []), + ]; + const requires = entry.parameters.flatMap((parameter) => + parameter.kind === "object" ? parameter.implements : [], + ); + for (const port of entry.dependencyPorts) { + if (portIds.has(port.id) || portNames.has(port.displayName)) + throw new GenericTypeError("duplicate-dependency-port", entry.displayName, "Duplicate port"); + portIds.add(port.id); + portNames.add(port.displayName); + const requirement = port.requirement; + if (requirement.kind === "state") { + if (requirement.primitives.some((p) => !["read", "write", "watch-start", "watch-stop"].includes(p))) + throw new GenericTypeError("invalid-port-primitive", port.displayName, "Invalid state primitive"); + types.push(requirement.valueType); + } + if (requirement.kind === "edge") types.push({ kind: "object-ref", expectation: requirement.target }); + if (requirement.kind === "constructor") + types.push(requirement.inputType, { kind: "object-ref", expectation: requirement.target }); + if (requirement.kind === "interface") requires.push(requirement.application); + } + if (entry.receiverRequirement.kind === "interfaces") requires.push(...entry.receiverRequirement.interfaces); + state.types.validateTemplate({ + interfaceId: capabilityId.interface(entry.id), + revisionId: capabilityId.interfaceRevision(entry.id), + displayName: entry.displayName, + source: resource.revision.source, + members: [], + template: { + parameters: entry.parameters, + aliases: entry.aliases, + usesSelf: false, + requires, + members: types.map((type, index) => ({ + kind: "value", + id: capabilityId.member(String(index)), + displayName: String(index), + valueType: type, + operations: [], + })), + }, + }); + } + } + resource.specializations = [...state.types.applications.values()]; + if (diagnostics.length > 0) { return { ok: false, diagnostics }; } @@ -1615,7 +2122,7 @@ export const compileCapabilityResourceSource = ( return { ok: true, resource, diagnostics: [] }; }; -export const compileCapabilitySource = ( +const compileCapabilitySourceInternal = ( source: string, fileName = "", environment: CapabilityImportEnvironment = {}, @@ -1666,3 +2173,35 @@ export const compileCapabilitySource = ( diagnostics: [], }; }; + +const genericDiagnostic = (error: GenericTypeError, fileName: string): CapabilitySourceDiagnostic => ({ + phase: "lowering", + code: error.code, + message: error.message, + fileName, + line: genericLocations.get(error)?.line ?? 0, + column: genericLocations.get(error)?.column ?? 0, + path: error.path, +}); + +export const compileCapabilityResourceSource = ( + ...args: Parameters +): CapabilityResourceCompileResult => { + try { + return compileCapabilityResourceSourceInternal(...args); + } catch (error) { + if (!(error instanceof GenericTypeError)) throw error; + return { ok: false, diagnostics: [genericDiagnostic(error, args[1].fileName ?? "")] }; + } +}; + +export const compileCapabilitySource = ( + ...args: Parameters +): CapabilitySourceCompileResult => { + try { + return compileCapabilitySourceInternal(...args); + } catch (error) { + if (!(error instanceof GenericTypeError)) throw error; + return { ok: false, diagnostics: [genericDiagnostic(error, args[1] ?? "")] }; + } +}; diff --git a/src/capability-language/structural-plan.ts b/src/capability-language/structural-plan.ts index e2ce3d3..e389fcb 100644 --- a/src/capability-language/structural-plan.ts +++ b/src/capability-language/structural-plan.ts @@ -28,7 +28,7 @@ type Change = { file: string; before: string | null; after: string; mode: number type Journal = { schemaVersion: 1; id: string; root: string; phase: "prepared" | "complete"; changes: Change[] }; const safeFile = (file: string) => { if ( - !/^(?:[A-Za-z0-9_-][A-Za-z0-9_.-]*\/)*(?:\.gitignore|\.yarnrc.yml|[A-Za-z0-9_-][A-Za-z0-9_.-]*\.(?:qx|lock|ts|tsx|css|mjs|json|nix|txtpb))$/.test( + !/^(?:[A-Za-z0-9_-][A-Za-z0-9_.-]*\/)*(?:\.gitignore|\.yarnrc.yml|[A-Za-z0-9_-][A-Za-z0-9_.-]*\.(?:qx|lock|ts|tsx|css|mjs|json|nix|txtpb|md))$/.test( file, ) || file.split("/").some((part) => [".git", ".jj", ".quixos", "node_modules"].includes(part)) diff --git a/src/capability-language/workspace-cli.ts b/src/capability-language/workspace-cli.ts index bf870b8..f56f80b 100644 --- a/src/capability-language/workspace-cli.ts +++ b/src/capability-language/workspace-cli.ts @@ -4,6 +4,7 @@ import { readFile, writeFile } from "node:fs/promises"; import process from "node:process"; import { compileWorkspaceRepository } from "./assembly.js"; import { createGitCapabilityResolver } from "./git-resolver.js"; +import { bindingSchema, specializeBindingSchema } from "../bindings/index.js"; import { planEvolution, runtimeContracts, @@ -14,7 +15,7 @@ import { const usage = `usage: quixos-workspace-compile --root DIRECTORY --checkout-root DIRECTORY [--snapshot-map PATH] [--graph-out PATH] [--workspace-id ID] [--workspace-revision-id ID] [--source-root-commit GIT_REV] [--baseline PLAN_JSON] [--evolution-out PATH] - [--reviews REVIEW_JSON] + [--reviews REVIEW_JSON] [--schemas-out PATH] Resolves a workspace's recursive resource-lock graph, clones every exact resource revision, validates standalone interface/package manifests, and emits @@ -35,6 +36,7 @@ const parseArgs = (args: string[]) => { rootDirectory, checkoutRoot, graphOut: values.get("--graph-out"), + schemasOut: values.get("--schemas-out"), snapshotMap: values.get("--snapshot-map"), workspaceId: values.get("--workspace-id"), workspaceRevisionId: values.get("--workspace-revision-id"), @@ -96,6 +98,24 @@ const main = async () => { ); } const candidate = { ...assembled.workspace, executionContracts: runtimeContracts(assembled.workspace) }; + if (options.schemasOut) { + const schemas: Record = {}; + for (const node of assembled.resources.filter((entry) => entry.kind === "package")) { + const closure = new Map(); + const visit = (entry: typeof node) => { + if (closure.has(entry.key)) return; + closure.set(entry.key, entry); + entry.dependencies.forEach(visit); + }; + visit(node); + schemas[node.resource.revision.revisionId] = specializeBindingSchema( + bindingSchema({ resources: [...closure.values()] }), + candidate, + node.resource.revision.revisionId, + ); + } + await writeFile(options.schemasOut, JSON.stringify(schemas)); + } if (options.evolutionOut) { const baseline = options.baseline ? (JSON.parse(await readFile(options.baseline, "utf8")) as WorkspaceRevision) diff --git a/src/capability-model/evolution.ts b/src/capability-model/evolution.ts index 31873f8..e9b1747 100644 --- a/src/capability-model/evolution.ts +++ b/src/capability-model/evolution.ts @@ -184,6 +184,9 @@ export const runtimeContracts = (workspace: WorkspaceRevision): RuntimeContract[ materializations: sorted(conformance.relationshipMaterializations, (entry) => entry.memberId), }); parent.dependencies.add(`interface:${conformance.interfaceRevisionId}`); + const contract = workspace.interfaceImports.find((entry) => entry.revisionId === conformance.interfaceRevisionId); + for (const required of contract?.requiredInterfaces ?? []) + parent.dependencies.add(conformanceKey(conformance.atomId, required)); for (const attachment of conformance.privateAttachments) parent.dependencies.add(`attachment:${attachment.id}`); for (const operation of conformance.operationBindings) binding(parent, operation.binding, conformance.atomId, { diff --git a/src/capability-model/generic-packages.ts b/src/capability-model/generic-packages.ts new file mode 100644 index 0000000..5bcb0fc --- /dev/null +++ b/src/capability-model/generic-packages.ts @@ -0,0 +1,141 @@ +import { createHash } from "node:crypto"; +import { + capabilityId, + type PackageExport, + type PackageExportId, + type DependencyPort, + type PackageRevision, +} from "./types.js"; +import { + TypeSubstitution, + GenericTypeError, + bindTypeParameters, + canonicalTypeArgument, + type TypeParameter, + type ValueTypeExpression, + type ObjectTypeExpression, + type InterfaceApplicationExpression, + type GenericTypeEnvironment, + type ClosedTypeArgument, + type ValueAliasDefinition, +} from "./generics.js"; + +export type GenericDependencyPort = Omit & { + requirement: + | { + kind: "state"; + valueType: ValueTypeExpression; + primitives: Extract["primitives"]; + } + | { + kind: "edge"; + target: ObjectTypeExpression; + cardinality: Extract["cardinality"]; + primitives: Extract["primitives"]; + } + | { kind: "interface"; application: InterfaceApplicationExpression } + | { kind: "constructor"; target: ObjectTypeExpression; inputType: ValueTypeExpression }; +}; + +export interface GenericPackageExport { + id: PackageExportId; + displayName: string; + parameters: TypeParameter[]; + kind: "operation" | "function"; + inputType: ValueTypeExpression; + outputType: ValueTypeExpression; + eventType?: ValueTypeExpression; + mode?: Extract["mode"]; + receiverRequirement: + | { kind: "any-object" } + | { kind: "target"; target: ObjectTypeExpression } + | { kind: "interfaces"; interfaces: InterfaceApplicationExpression[] }; + dependencyPorts: GenericDependencyPort[]; + aliases: ValueAliasDefinition[]; +} + +/** A closed manifest is a Nix build input, never a mutable source checkout. */ +export const specializePackageExport = ( + pkg: PackageRevision, + definition: GenericPackageExport, + arguments_: ClosedTypeArgument[], + environment: GenericTypeEnvironment, +): PackageExport => { + const lexical = { ...environment, aliases: new Map(definition.aliases.map((alias) => [alias.id, alias])) }; + const bindings = bindTypeParameters(definition.parameters, arguments_, lexical, definition.displayName); + const substitution = new TypeSubstitution({ ...lexical, arguments: bindings }); + const digest = createHash("sha256") + .update( + JSON.stringify([ + "quixos-package-specialization-v1", + pkg.revisionId, + [pkg.source.repository, pkg.source.commit], + definition.id, + arguments_.map(canonicalTypeArgument), + environment.self ?? null, + ]), + ) + .digest("hex"); + const ports: DependencyPort[] = definition.dependencyPorts.map((port) => { + const requirement = port.requirement; + switch (requirement.kind) { + case "state": + return { ...port, requirement: { ...requirement, valueType: substitution.value(requirement.valueType) } }; + case "edge": + return { ...port, requirement: { ...requirement, target: substitution.object(requirement.target) } }; + case "interface": + return { + ...port, + requirement: { kind: "interface", interfaceRevisionId: substitution.application(requirement.application) }, + }; + case "constructor": { + const target = substitution.object(requirement.target); + if (target.kind !== "atom") + throw new GenericTypeError( + "constructor-target", + port.displayName, + "Constructor ports require a concrete atom argument, not an interface view", + ); + return { + ...port, + requirement: { + kind: "constructor", + atomId: target.atomId, + inputType: substitution.value(requirement.inputType), + }, + }; + } + } + }); + const common = { + id: capabilityId.packageExport(`export-application:sha256:${digest}`), + displayName: `${definition.displayName}$${digest}`, + inputType: substitution.value(definition.inputType), + outputType: substitution.value(definition.outputType), + dependencyPorts: ports, + application: { + exportId: definition.id, + arguments: structuredClone(arguments_), + ...(environment.self ? { self: environment.self } : {}), + }, + }; + if (definition.kind === "function") return { ...common, kind: "function" }; + const receiver = definition.receiverRequirement; + const target = receiver.kind === "target" ? substitution.object(receiver.target) : undefined; + return { + ...common, + kind: "operation", + mode: definition.mode!, + ...(definition.eventType ? { eventType: substitution.value(definition.eventType) } : {}), + receiverRequirement: target + ? target.kind === "atom" + ? { kind: "exact-atom", atomId: target.atomId } + : { kind: "all-interfaces", interfaceRevisionIds: [target.interfaceRevisionId] } + : receiver.kind === "interfaces" + ? { + kind: "all-interfaces", + interfaceRevisionIds: receiver.interfaces.map((value) => substitution.application(value)), + } + : { kind: "any-object" }, + }; +}; diff --git a/src/capability-model/generics.ts b/src/capability-model/generics.ts new file mode 100644 index 0000000..3b592aa --- /dev/null +++ b/src/capability-model/generics.ts @@ -0,0 +1,420 @@ +import { createHash } from "node:crypto"; +import { + capabilityId, + type AtomId, + type InterfaceMember, + type InterfaceOperation, + type InterfaceRevision, + type InterfaceRevisionId, + type ObjectExpectation, + type SourceRevision, + type ValueType, +} from "./types.js"; + +/** Authoring-only expressions. Installed ValueType deliberately has no variable case. */ +export type ValueTypeExpression = + | Extract + | { kind: "parameter"; parameterId: string } + | { kind: "record"; fields: Record } + | { kind: "list" | "optional"; value: ValueTypeExpression } + | { kind: "object-ref"; expectation: ObjectTypeExpression } + | { kind: "alias"; definitionId: string; arguments: TypeArgumentExpression[] }; + +export type ObjectTypeExpression = + | { kind: "atom"; atomId: AtomId } + | { kind: "interface"; interfaceRevisionId: InterfaceRevisionId } + | { kind: "parameter"; parameterId: string } + | { kind: "self" } + | { kind: "application"; application: InterfaceApplicationExpression }; + +export type TypeArgumentExpression = + | { kind: "value"; type: ValueTypeExpression } + | { kind: "object"; target: ObjectTypeExpression }; + +export type ClosedTypeArgument = { kind: "value"; type: ValueType } | { kind: "object"; target: ObjectExpectation }; + +export interface InterfaceApplicationExpression { + definitionId: InterfaceRevisionId; + arguments: TypeArgumentExpression[]; +} + +export type TypeParameter = + | { id: string; name: string; kind: "value"; storable?: boolean } + | { id: string; name: string; kind: "object"; implements: InterfaceApplicationExpression[] }; + +export interface ValueAliasDefinition { + id: string; + parameters: TypeParameter[]; + body: ValueTypeExpression; +} + +export interface GenericInterfaceTemplate { + parameters: TypeParameter[]; + members: InterfaceMember[]; + requires: InterfaceApplicationExpression[]; + usesSelf: boolean; + aliases?: ValueAliasDefinition[]; +} + +/** Instantiation produces the existing closed runtime IR, with explicit provenance. */ +export const instantiateInterface = ( + definition: InterfaceRevision, + arguments_: readonly ClosedTypeArgument[], + environment: GenericTypeEnvironment, +): InterfaceRevision => { + const template = definition.template; + if (!template) { + if (arguments_.length) fail("type-arity", definition.displayName, "Non-generic interface takes no type arguments"); + return definition; + } + const lexicalEnvironment = { + ...environment, + aliases: new Map((template.aliases ?? []).map((alias) => [alias.id, alias])), + }; + const argumentsMap = bindTypeParameters(template.parameters, arguments_, lexicalEnvironment, definition.displayName); + if (template.usesSelf && !environment.self) + fail("unbound-self", definition.displayName, "Self requires an implementing atom"); + const substitution = new TypeSubstitution({ ...lexicalEnvironment, arguments: argumentsMap }); + const operation = (entry: InterfaceOperation): InterfaceOperation => ({ + ...entry, + inputType: substitution.value(entry.inputType, `${definition.displayName}.${entry.displayName}.input`), + outputType: substitution.value(entry.outputType, `${definition.displayName}.${entry.displayName}.output`), + eventType: entry.eventType + ? substitution.value(entry.eventType, `${definition.displayName}.${entry.displayName}.event`) + : undefined, + }); + const members: InterfaceMember[] = template.members.map((member) => { + const common = { id: member.id, displayName: member.displayName, operations: member.operations.map(operation) }; + switch (member.kind) { + case "value": + return { ...common, kind: "value", valueType: substitution.value(member.valueType, member.displayName) }; + case "relationship": + return { + ...common, + kind: "relationship", + target: substitution.object(member.target, member.displayName), + cardinality: member.cardinality, + ordered: member.ordered, + }; + case "operation": + return { + ...common, + kind: "operation", + inputType: substitution.value(member.inputType, member.displayName), + outputType: substitution.value(member.outputType, member.displayName), + }; + } + }); + const application: AppliedInterfaceIdentity = { + definitionId: definition.revisionId, + source: definition.source, + arguments: structuredClone([...arguments_]), + ...(template.usesSelf ? { self: environment.self } : {}), + }; + return { + interfaceId: definition.interfaceId, + revisionId: appliedInterfaceId(application), + displayName: definition.displayName, + source: definition.source, + members, + application, + requiredInterfaces: template.requires.map((required) => substitution.application(required)), + }; +}; + +export interface GenericTypeEnvironment { + arguments: ReadonlyMap; + self?: AtomId; + aliases?: ReadonlyMap; + /** Resolves only checked declarations, never a caller-supplied runtime type string. */ + applyInterface: (definitionId: InterfaceRevisionId, arguments_: readonly ClosedTypeArgument[]) => InterfaceRevisionId; + /** Proof in the candidate, not an authorization grant. */ + implementsInterface: (target: ObjectExpectation, required: InterfaceRevisionId) => boolean; + /** External codecs must explicitly declare reference-free persistence support. */ + storableMessage?: (descriptorId: string) => boolean; +} + +export class GenericTypeError extends Error { + constructor( + readonly code: string, + readonly path: string, + message: string, + ) { + super(`${path}: ${message}`); + this.name = "GenericTypeError"; + } +} + +const fail = (code: string, path: string, message: string): never => { + throw new GenericTypeError(code, path, message); +}; + +/** One budget across nested aliases/substitutions, including concrete arguments. */ +class Budget { + private remaining = 10000; + enter(path: string, depth: number) { + if (depth > 128 || --this.remaining < 0) + fail("type-complexity-limit", path, "Type exceeds the depth or expansion budget"); + } +} + +export const isStorableType = ( + type: ValueType, + storableMessage: (descriptorId: string) => boolean = () => false, +): boolean => { + const budget = new Budget(); + const visit = (value: ValueType, depth: number): boolean => { + budget.enter("storable", depth); + switch (value.kind) { + case "scalar": + return true; + case "builtin": + return value.name === "unit"; + case "message": + return storableMessage(value.descriptorId); + case "object-ref": + return false; + case "list": + case "optional": + return visit(value.value, depth + 1); + // Records currently have RPC codecs, not ordinary-state persistence codecs. + // A generic bound must not promise storage that the installed model rejects. + case "record": + return false; + } + }; + return visit(type, 0); +}; + +/** Canonical closed-type encoding, independent of record insertion order. */ +export const canonicalTypeArgument = (argument: ClosedTypeArgument): string => { + const budget = new Budget(); + const target = (value: ObjectExpectation): unknown => { + switch (value.kind) { + case "atom": + return ["atom", value.atomId]; + case "interface": + return ["interface", value.interfaceRevisionId]; + default: + return fail("unresolved-type", "argument", "Expected a closed object target"); + } + }; + const type = (value: ValueType, depth: number): unknown => { + budget.enter("argument", depth); + switch (value.kind) { + case "builtin": + case "scalar": + return [value.kind, value.name]; + case "message": + return ["message", value.descriptorId]; + case "object-ref": + return ["object-ref", target(value.expectation)]; + case "list": + case "optional": + return [value.kind, type(value.value, depth + 1)]; + case "record": + return [ + "record", + Object.keys(value.fields) + .sort() + .map((key) => [key, type(value.fields[key], depth + 1)]), + ]; + default: + return fail("unresolved-type", "argument", "Expected a closed value type"); + } + }; + switch (argument.kind) { + case "value": + return JSON.stringify(["value", type(argument.type, 0)]); + case "object": + return JSON.stringify(["object", target(argument.target)]); + default: + return fail("invalid-kind", "argument", "Expected a value or object type argument"); + } +}; + +export interface AppliedInterfaceIdentity { + definitionId: InterfaceRevisionId; + source: SourceRevision; + arguments: ClosedTypeArgument[]; + /** Only include Self when it is actually part of the closed contract. */ + self?: AtomId; +} + +export const appliedInterfaceId = (application: AppliedInterfaceIdentity): InterfaceRevisionId => { + const encoding = JSON.stringify([ + "quixos-applied-interface-v1", + application.definitionId, + application.source.repository, + application.source.commit.toLowerCase(), + application.arguments.map(canonicalTypeArgument), + application.self ?? null, + ]); + return capabilityId.interfaceRevision( + `interface-application:sha256:${createHash("sha256").update(encoding).digest("hex")}`, + ); +}; + +export class TypeSubstitution { + private readonly budget = new Budget(); + private readonly aliases: string[] = []; + + constructor(private environment: GenericTypeEnvironment) {} + + argument(expression: TypeArgumentExpression, path = "argument", depth = 0): ClosedTypeArgument { + this.budget.enter(path, depth); + switch (expression.kind) { + case "value": + return { kind: "value", type: this.value(expression.type, path, depth + 1) }; + case "object": + return { kind: "object", target: this.object(expression.target, path, depth + 1) }; + default: + return fail("invalid-kind", path, "Expected a value or object type argument"); + } + } + + application(expression: InterfaceApplicationExpression, path = "interface", depth = 0): InterfaceRevisionId { + this.budget.enter(path, depth); + return this.environment.applyInterface( + expression.definitionId, + expression.arguments.map((argument, index) => this.argument(argument, `${path}.arguments[${index}]`, depth + 1)), + ); + } + + object(expression: ObjectTypeExpression, path = "target", depth = 0): ObjectExpectation { + this.budget.enter(path, depth); + switch (expression.kind) { + case "atom": + return { kind: "atom", atomId: expression.atomId }; + case "interface": + return { kind: "interface", interfaceRevisionId: expression.interfaceRevisionId }; + case "self": + return this.environment.self + ? { kind: "atom", atomId: this.environment.self } + : fail("unbound-self", path, "Self requires an implementing atom"); + case "parameter": { + const argument = this.environment.arguments.get(expression.parameterId); + if (!argument) return fail("unbound-parameter", path, `Unbound parameter ${expression.parameterId}`); + if (argument.kind !== "object") + return fail("parameter-kind", path, `Parameter ${expression.parameterId} is a value, not an object target`); + canonicalTypeArgument(argument); + return structuredClone(argument.target); + } + case "application": + return { kind: "interface", interfaceRevisionId: this.application(expression.application, path, depth + 1) }; + default: + return fail("invalid-type", path, "Unknown object type expression"); + } + } + + value(expression: ValueTypeExpression, path = "type", depth = 0): ValueType { + this.budget.enter(path, depth); + switch (expression.kind) { + case "builtin": + return { kind: "builtin", name: expression.name }; + case "scalar": + return { kind: "scalar", name: expression.name }; + case "message": + return { kind: "message", descriptorId: expression.descriptorId }; + case "list": + case "optional": + return { kind: expression.kind, value: this.value(expression.value, `${path}.${expression.kind}`, depth + 1) }; + case "record": + return { + kind: "record", + fields: Object.fromEntries( + Object.entries(expression.fields).map(([name, field]) => [ + name, + this.value(field, `${path}.${name}`, depth + 1), + ]), + ), + }; + case "object-ref": + return { kind: "object-ref", expectation: this.object(expression.expectation, `${path}.ref`, depth + 1) }; + case "parameter": { + const argument = this.environment.arguments.get(expression.parameterId); + if (!argument) return fail("unbound-parameter", path, `Unbound parameter ${expression.parameterId}`); + if (argument.kind !== "value") + return fail("parameter-kind", path, `Parameter ${expression.parameterId} is an object target; use ref`); + canonicalTypeArgument(argument); + return this.value(argument.type, path, depth + 1); + } + case "alias": { + const alias = this.environment.aliases?.get(expression.definitionId); + if (!alias) return fail("unknown-alias", path, `Unknown type alias ${expression.definitionId}`); + if (this.aliases.includes(alias.id)) + return fail("recursive-alias", path, `Recursive value alias: ${[...this.aliases, alias.id].join(" -> ")}`); + const arguments_ = expression.arguments.map((argument, index) => + this.argument(argument, `${path}.arguments[${index}]`, depth + 1), + ); + const bindings = bindTypeParameters(alias.parameters, arguments_, this.environment, path); + this.aliases.push(alias.id); + try { + // Reuse this expansion budget/stack; lexical parameter maps are restored. + const previous = this.environment; + this.environment = { ...previous, arguments: bindings }; + try { + return this.value(alias.body, `${path}.${alias.id}`, depth + 1); + } finally { + this.environment = previous; + } + } finally { + this.aliases.pop(); + } + } + default: + return fail("invalid-type", path, "Unknown value type expression"); + } + } +} + +export const bindTypeParameters = ( + parameters: readonly TypeParameter[], + arguments_: readonly ClosedTypeArgument[], + environment: GenericTypeEnvironment, + path = "parameters", +): ReadonlyMap => { + if (parameters.length !== arguments_.length) + fail("type-arity", path, `Expected ${parameters.length} type arguments, received ${arguments_.length}`); + const bindings = new Map(); + const names = new Set(); + for (const [index, parameter] of parameters.entries()) { + if ( + !parameter.id || + !parameter.name || + parameter.name === "Self" || + bindings.has(parameter.id) || + names.has(parameter.name) + ) + fail("duplicate-parameter", path, `Invalid or duplicate parameter ${parameter.name}`); + names.add(parameter.name); + const argument = arguments_[index]; + if (parameter.kind !== argument.kind) + fail("parameter-kind", `${path}.${parameter.name}`, `Expected ${parameter.kind}, received ${argument.kind}`); + canonicalTypeArgument(argument); + bindings.set(parameter.id, structuredClone(argument)); + } + const substitution = new TypeSubstitution({ ...environment, arguments: bindings }); + for (const parameter of parameters) { + const argument = bindings.get(parameter.id)!; + if ( + parameter.kind === "value" && + argument.kind === "value" && + parameter.storable && + !isStorableType(argument.type, environment.storableMessage) + ) + fail( + "non-storable-argument", + `${path}.${parameter.name}`, + "State values cannot contain managed references or unsupported transport values", + ); + if (parameter.kind === "object" && argument.kind === "object") { + for (const bound of parameter.implements) { + const required = substitution.application(bound, `${path}.${parameter.name}.implements`); + if (!environment.implementsInterface(argument.target, required)) + fail("unsatisfied-bound", `${path}.${parameter.name}`, `Object target does not implement ${required}`); + } + } + } + return bindings; +}; diff --git a/src/capability-model/index.ts b/src/capability-model/index.ts index d1055f3..30449d6 100644 --- a/src/capability-model/index.ts +++ b/src/capability-model/index.ts @@ -2,3 +2,5 @@ export * from "./types.js"; export * from "./validation.js"; export * from "./evolution.js"; export * from "./migrations.js"; +export * from "./generics.js"; +export * from "./generic-packages.js"; diff --git a/src/capability-model/types.ts b/src/capability-model/types.ts index b5ef4c1..2f2c7aa 100644 --- a/src/capability-model/types.ts +++ b/src/capability-model/types.ts @@ -104,25 +104,25 @@ export interface AtomDefinition { export type InterfaceOperationMode = "call" | "watch-start" | "watch-stop" | "subscribe" | "unsubscribe"; -export interface InterfaceOperation { +export interface InterfaceOperation { id: OperationId; displayName: string; - inputType: ValueType; - outputType: ValueType; + inputType: Type; + outputType: Type; mode: InterfaceOperationMode; /** Required for watch-start/subscribe and absent for other modes. */ - eventType?: ValueType; + eventType?: Type; } -interface InterfaceMemberBase { +interface InterfaceMemberBase { id: MemberId; displayName: string; - operations: InterfaceOperation[]; + operations: InterfaceOperation[]; } -export interface ValueInterfaceMember extends InterfaceMemberBase { +export interface ValueInterfaceMember extends InterfaceMemberBase { kind: "value"; - valueType: ValueType; + valueType: Type; } export type EdgeCardinality = "optional-one" | "exactly-one" | "many" | "many-unique"; @@ -134,21 +134,27 @@ export type EdgeEndpointConstraint = interfaceRevisionId: InterfaceRevisionId; }; -export interface RelationshipInterfaceMember extends InterfaceMemberBase { +export interface RelationshipInterfaceMember< + Type = ValueType, + Target = EdgeEndpointConstraint, +> extends InterfaceMemberBase { kind: "relationship"; - target: EdgeEndpointConstraint; + target: Target; cardinality: EdgeCardinality; ordered: boolean; } /** A named callable capability that is not value or relationship sugar. */ -export interface OperationInterfaceMember extends InterfaceMemberBase { +export interface OperationInterfaceMember extends InterfaceMemberBase { kind: "operation"; - inputType: ValueType; - outputType: ValueType; + inputType: Type; + outputType: Type; } -export type InterfaceMember = ValueInterfaceMember | RelationshipInterfaceMember | OperationInterfaceMember; +export type InterfaceMember = + | ValueInterfaceMember + | RelationshipInterfaceMember + | OperationInterfaceMember; export interface InterfaceRevision { interfaceId: InterfaceId; @@ -156,6 +162,12 @@ export interface InterfaceRevision { displayName: string; source: SourceRevision; members: InterfaceMember[]; + /** Authored generic definition; never an executable contract by itself. */ + template?: import("./generics.js").GenericInterfaceTemplate; + /** Immutable provenance of a closed generic application. */ + application?: import("./generics.js").AppliedInterfaceIdentity; + requiredInterfaces?: InterfaceRevisionId[]; + argumentRequirements?: { target: ObjectExpectation; required: InterfaceRevisionId }[]; } export type StoragePolicy = { kind: "optimistic-register" } | { kind: "crdt-document"; updateType: ValueType }; @@ -233,6 +245,8 @@ interface PackageExportBase { inputType: ValueType; outputType: ValueType; dependencyPorts: DependencyPort[]; + /** Closed adapter served by the same immutable package build. */ + application?: { exportId: PackageExportId; arguments: import("./generics.js").ClosedTypeArgument[]; self?: AtomId }; } export interface PackageOperationExport extends PackageExportBase { @@ -254,6 +268,8 @@ export interface PackageConstructorExport extends PackageExportBase { export type PackageExport = PackageOperationExport | PackageFunctionExport | PackageConstructorExport; export interface PackageRevision { + genericExports?: import("./generic-packages.js").GenericPackageExport[]; + argumentRequirements?: { target: ObjectExpectation; required: InterfaceRevisionId }[]; migrationCatalog?: import("./migrations.js").MigrationCatalog; packageId: PackageId; revisionId: PackageRevisionId; diff --git a/src/capability-model/validation.ts b/src/capability-model/validation.ts index 6597082..65c1b47 100644 --- a/src/capability-model/validation.ts +++ b/src/capability-model/validation.ts @@ -34,6 +34,9 @@ import type { WorkspaceRevision, } from "./types.js"; import { valueType } from "./types.js"; +import { appliedInterfaceId, GenericTypeError } from "./generics.js"; +import { specializePackageExport } from "./generic-packages.js"; +import { isDeepStrictEqual } from "node:util"; export type CapabilityValidationIssueCode = | "invalid-semantic-major" @@ -217,7 +220,12 @@ const validateValueType = ( } return; case "builtin": + if (!["unit", "watch-handle"].includes(type.name)) + issue(issues, "invalid-value-type", path, "Unknown builtin value type"); + return; case "scalar": + if (!["bool", "bytes", "double", "int32", "int64", "string", "uint32", "uint64"].includes(type.name)) + issue(issues, "invalid-value-type", path, "Unknown scalar value type"); return; case "message": requireText(issues, type.descriptorId, `${path}.descriptorId`, "Message descriptor identity"); @@ -244,6 +252,14 @@ const validateValueType = ( `Unknown interface revision ${type.expectation.interfaceRevisionId}`, ); } + return; + default: + issue( + issues, + "invalid-value-type", + path, + "Installed contracts require closed value types; unresolved authoring expressions are not executable", + ); } }; @@ -458,9 +474,29 @@ const collectIdentityIndexes = ( for (const [interfaceIndex, revision] of workspace.interfaceImports.entries()) { const path = `interfaceImports[${interfaceIndex}]`; requireText(issues, revision.interfaceId, `${path}.interfaceId`, "Interface ID"); + if (revision.template) + issue(issues, "invalid-value-type", path, "Unapplied generic interface cannot enter an installed workspace"); requireText(issues, revision.revisionId, `${path}.revisionId`, "Interface revision ID"); requireText(issues, revision.displayName, `${path}.displayName`, "Interface name"); validateSource(issues, revision.source, `${path}.source`); + if (revision.application) { + try { + if ( + appliedInterfaceId(revision.application) !== revision.revisionId || + revision.application.source.repository !== revision.source.repository || + revision.application.source.commit !== revision.source.commit + ) + issue( + issues, + "invalid-value-type", + path, + "Applied interface identity does not match its exact provenance and arguments", + ); + } catch (error) { + if (!(error instanceof GenericTypeError)) throw error; + issue(issues, "invalid-value-type", path, error.message); + } + } const memberIds = new Set(); const operations = new Map(); for (const [memberIndex, member] of revision.members.entries()) { @@ -516,6 +552,35 @@ const collectIdentityIndexes = ( const exports = new Map(); for (const [exportIndex, entry] of revision.exports.entries()) { const exportPath = `${path}.exports[${exportIndex}]`; + if (entry.application) { + try { + const definition = revision.genericExports?.find((candidate) => candidate.id === entry.application!.exportId); + if (!definition) throw new Error("Missing generic export definition"); + const expected = specializePackageExport(revision, definition, entry.application.arguments, { + arguments: new Map(), + self: entry.application.self, + applyInterface: (id, args) => { + const applied = workspace.interfaceImports.find( + (contract) => + contract.application?.definitionId === id && isDeepStrictEqual(contract.application.arguments, args), + ); + if (applied) return applied.revisionId; + const concrete = workspace.interfaceImports.find((contract) => contract.revisionId === id); + if (concrete && !concrete.template && args.length === 0) return id; + throw new Error(`Missing closed interface application ${id}`); + }, + // Retained obligations are discharged against candidate conformances below. + implementsInterface: (target, required) => + (revision.argumentRequirements ?? []).some( + (obligation) => obligation.required === required && isDeepStrictEqual(obligation.target, target), + ), + }); + if (!isDeepStrictEqual(entry, expected)) + throw new Error("Specialized export differs from its definition, source or arguments"); + } catch (error) { + issue(issues, "invalid-operation", exportPath, error instanceof Error ? error.message : String(error)); + } + } requireText(issues, entry.id, `${exportPath}.id`, "Package export ID"); requireText(issues, entry.displayName, `${exportPath}.displayName`, "Package export name"); if (exports.has(entry.id)) { @@ -655,8 +720,54 @@ const validateInterfaces = ( issues: CapabilityValidationIssue[], indexes: ValidationIndexes, ) => { + const implies = ( + actual: InterfaceRevisionId, + required: InterfaceRevisionId, + visited = new Set(), + ): boolean => { + if (actual === required) return true; + if (visited.has(actual)) return false; + visited.add(actual); + return (indexes.interfaces.get(actual)?.revision.requiredInterfaces ?? []).some((entry) => + implies(entry, required, visited), + ); + }; + for (const [packageIndex, pkg] of workspace.packageImports.entries()) { + for (const obligation of pkg.argumentRequirements ?? []) { + const satisfied = + obligation.target.kind === "atom" + ? indexes.conformances.has(conformanceKey(obligation.target.atomId, obligation.required)) + : implies(obligation.target.interfaceRevisionId, obligation.required); + if (!satisfied) + issue( + issues, + "unsatisfied-interface", + `packageImports[${packageIndex}]`, + `Generic argument does not implement ${obligation.required}`, + ); + } + } for (const [interfaceIndex, revision] of workspace.interfaceImports.entries()) { const path = `interfaceImports[${interfaceIndex}]`; + for (const required of revision.requiredInterfaces ?? []) { + if (!indexes.interfaces.has(required)) + issue(issues, "unresolved-reference", path, `Unknown prerequisite interface ${required}`); + else if (implies(required, revision.revisionId)) + issue( + issues, + "cyclic-conformance-requirement", + path, + `Cyclic prerequisite involving ${revision.revisionId} and ${required}`, + ); + } + for (const obligation of revision.argumentRequirements ?? []) { + const satisfied = + obligation.target.kind === "atom" + ? indexes.conformances.has(conformanceKey(obligation.target.atomId, obligation.required)) + : implies(obligation.target.interfaceRevisionId, obligation.required); + if (!satisfied) + issue(issues, "unsatisfied-interface", path, `Generic argument does not implement ${obligation.required}`); + } for (const [memberIndex, member] of revision.members.entries()) { const memberPath = `${path}.members[${memberIndex}]`; if (member.kind === "value") { @@ -1282,6 +1393,12 @@ const validateConformances = ( } const bindings = new Map(); + for (const required of interfaceEntry.revision.requiredInterfaces ?? []) { + const requiredKey = conformanceKey(conformance.atomId, required); + if (!indexes.conformances.has(requiredKey)) + issue(issues, "unsatisfied-interface", path, `Conformance requires ${conformance.atomId} as ${required}`); + else requirementGraph.get(key)?.add(requiredKey); + } for (const [bindingIndex, entry] of conformance.operationBindings.entries()) { const bindingPath = `${path}.operationBindings[${bindingIndex}]`; if (bindings.has(entry.operationId)) { @@ -1957,6 +2074,9 @@ export const computeCapabilityClosure = ( atomId: conformance.source.atomId, interfaceRevisionId: conformance.source.interfaceRevisionId, }); + for (const interfaceRevisionId of plan.interfaces.get(root.interfaceRevisionId)?.requiredInterfaces ?? []) { + queued.push({ atomId: root.atomId, interfaceRevisionId }); + } for (const binding of conformance.operationBindings.values()) { if (binding.kind === "state") { attachments.add(binding.slotId); diff --git a/test/generic-packages.test.ts b/test/generic-packages.test.ts new file mode 100644 index 0000000..1d8525c --- /dev/null +++ b/test/generic-packages.test.ts @@ -0,0 +1,199 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { execFile } from "node:child_process"; +import { promisify } from "node:util"; +import { createRequire } from "node:module"; +import { compileCapabilityResourceSource, compileCapabilitySource } from "../src/capability-language/parser.js"; +import { generateTypeScriptBindings, generatePackageDescriptor } from "../src/bindings/index.js"; +import { genericImplementationType } from "../src/bindings/generics.js"; +import { compileWorkspaceRevision, runtimeContracts } from "../src/capability-model/index.js"; +import { generateAppliedClientContracts } from "../src/bindings/client.js"; +const source = { repository: "https://example.test/generic.git", commit: "a".repeat(40) }; +test("generic port aliases retain their defining scope, including shadowed alias names", () => { + const iface = compileCapabilityResourceSource( + 'type Box = list; interface Data id "data" revision "data@1" {value payload id "payload" : Box {get id "payload:get";}}', + { source }, + ); + assert.ok(iface.ok, JSON.stringify(iface.diagnostics)); + if (iface.resource.kind !== "interface") throw new Error("expected interface"); + const pkg = compileCapabilityResourceSource( + 'import interface Data; type Box = optional; package P id "p" revision "p@1" {operation fetch id "data@1" : unit -> list> mode call receiver any requires {interface data id "data" : Data>;};}', + { source, environment: { interfaces: new Map([["Data", iface.resource.revision]]) } }, + ); + assert.ok(pkg.ok, JSON.stringify(pkg.diagnostics)); + if (pkg.resource.kind !== "package") throw new Error("expected package"); + const generated = generateTypeScriptBindings( + { + format: "quixos-bindings", + version: 1, + interfaces: [], + interfaceTemplates: [iface.resource.revision], + packages: [pkg.resource.revision], + }, + pkg.resource.revision.revisionId, + ); + assert.match(generated, /"payload.get":\(\)=>Promise>/); +}); +const definition = () => { + const result = compileCapabilityResourceSource( + `package Generic id "generic" revision "generic@1" { + operation echo id "echo" : T -> T mode call receiver any; + }`, + { source }, + ); + assert.ok(result.ok, JSON.stringify(result.diagnostics)); + if (result.resource.kind !== "package") throw new Error("expected package"); + return result.resource.revision; +}; +test("generic package exports specialize per binding without changing immutable source identity", () => { + const iface = compileCapabilityResourceSource( + `interface Echo id "echo-interface" revision "echo-interface@1" { + operation echo id "echo-member" : T -> T {call id "echo-call";} + }`, + { source }, + ); + assert.ok(iface.ok); + if (iface.resource.kind !== "interface") throw new Error("expected interface"); + const pkg = definition(); + const result = compileCapabilitySource( + `workspace W id "w" revision "w@1" commit "${source.commit}" { + import interface Echo; import package Generic; atom Thing id "thing"; + conform Thing as Echo id "string-echo" {bind echo.call to package Generic.echo;} + conform Thing as Echo> id "list-echo" {bind echo.call to package Generic.echo>;} + }`, + "workspace.qx", + { interfaces: new Map([["Echo", iface.resource.revision]]), packages: new Map([["Generic", pkg]]) }, + ); + assert.ok(result.ok, JSON.stringify(result.diagnostics)); + assert.equal(pkg.exports.length, 0, "source definition was not mutated"); + const closed = result.workspace.packageImports[0]; + assert.equal(closed.revisionId, pkg.revisionId); + assert.equal(closed.exports.length, 2); + assert.notEqual(closed.exports[0].id, closed.exports[1].id); + const oneApplication = structuredClone(result.workspace); + oneApplication.packageImports[0].exports.splice(1); + oneApplication.conformances.splice(1); + assert.notDeepEqual( + runtimeContracts(oneApplication), + runtimeContracts(result.workspace), + "New specializations invalidate the executable contract even with unchanged package source", + ); + const schema = { + format: "quixos-bindings" as const, + version: 1 as const, + interfaces: result.workspace.interfaceImports, + packages: [closed], + }; + const generated = generateTypeScriptBindings(schema, pkg.revisionId); + assert.match(generated, /"echo":\s*\(/); + for (const entry of closed.exports) { + assert.ok(generated.includes(entry.id)); + assert.ok(generatePackageDescriptor(schema, pkg.revisionId).includes(entry.id)); + } + const forged = structuredClone(result.workspace); + forged.packageImports[0].exports[0].outputType = { kind: "scalar", name: "bool" }; + const rejected = compileWorkspaceRevision(forged); + assert.equal(rejected.ok, false); + if (!rejected.ok) assert.ok(rejected.issues.some((issue) => issue.message.includes("Specialized export differs"))); +}); +test("generated universal implementations compile and cannot assume a concrete value type", async () => { + const pkg = definition(); + const signature = genericImplementationType(pkg.genericExports![0], [], () => { + throw new Error("unexpected concrete type"); + }); + const directory = await fs.mkdtemp(path.join(os.tmpdir(), "qx-generic-ts-")); + const require = createRequire(import.meta.url); + const compiler = path.join(path.dirname(require.resolve("typescript/package.json")), "bin/tsc"); + const run = promisify(execFile); + const preamble = `type QxObjectRef={readonly identity:T}; type QxContextLifecycle={signal?:AbortSignal}; type Handler=${signature};\n`; + try { + const file = path.join(directory, "generic.ts"); + await fs.writeFile(file, preamble + `const handler:Handler=({input})=>input;`); + await run(process.execPath, [compiler, "--ignoreConfig", "--noEmit", "--strict", "--target", "es2023", file]); + await fs.writeFile(file, preamble + `const handler:Handler=({input})=>"not universally T";`); + await assert.rejects( + run(process.execPath, [compiler, "--ignoreConfig", "--noEmit", "--strict", "--target", "es2023", file]), + (error) => { + assert.match(String((error as { stdout: string }).stdout), /not assignable/); + return true; + }, + ); + } finally { + await fs.rm(directory, { recursive: true, force: true }); + } +}); + +test("generic Self follows the receiver parameter, never a preceding export", () => { + const compile = (declaration: string) => + compileCapabilityResourceSource(`package P id "p" revision "p@1" {${declaration}}`, { source }); + const valid = compile('operation identity id "identity" : unit -> ref mode call receiver object T;'); + assert.ok(valid.ok, JSON.stringify(valid.diagnostics)); + if (valid.resource.kind !== "package") throw new Error("expected package"); + const definition = valid.resource.revision.genericExports![0]; + assert.deepEqual(definition.outputType, { + kind: "object-ref", + expectation: { kind: "parameter", parameterId: definition.parameters[0].id }, + }); + assert.equal(compile('function identity id "identity" : T -> ref;').ok, false); + assert.equal( + compile('operation events id "events" : unit -> watch-handle mode watch-start receiver any;').ok, + false, + ); +}); + +test("typed presentation and factory consumers preserve distinct closed applications and return targets", async () => { + const interfaces = new Map(); + for (const text of [ + 'interface Presentation id "presentation" revision "presentation@1" {operation props id "props" : unit -> Props {call id "props:get";}}', + 'interface Factory id "factory" revision "factory@1" {operation create id "create" : unit -> ref {call id "create:call";}}', + ]) { + const result = compileCapabilityResourceSource(text, { source }); + assert.ok(result.ok, JSON.stringify(result.diagnostics)); + interfaces.set(result.resource.revision.displayName, result.resource.revision); + } + const pkg = compileCapabilityResourceSource( + `import interface Presentation; import interface Factory; + external atom Note id "note"; external atom Notebook id "notebook"; + package Views id "views" revision "views@1" { + operation note id "note-view" : unit -> unit mode call receiver any requires {interface props id "props" : Presentation;}>;}; + operation notebook id "notebook-view" : unit -> unit mode call receiver any requires {interface props id "props" : Presentation;}; + operation factory id "factory-view" : unit -> unit mode call receiver any requires {interface factory id "factory" : Factory;}; + }`, + { source, environment: { interfaces } }, + ); + assert.ok(pkg.ok, JSON.stringify(pkg.diagnostics)); + const closed = pkg.resource.specializations!; + const presentation = closed.find( + (contract) => + contract.application?.definitionId === "presentation@1" && JSON.stringify(contract).includes('"title"'), + )!; + const factory = closed.find((contract) => contract.application?.definitionId === "factory@1")!; + const contracts = generateAppliedClientContracts(closed); + const directory = await fs.mkdtemp(path.join(os.tmpdir(), "qx-presentation-types-")); + const compiler = path.join( + path.dirname(createRequire(import.meta.url).resolve("typescript/package.json")), + "bin/tsc", + ); + const run = promisify(execFile); + const usage = `type Props=CapabilityOutput<${JSON.stringify(presentation.revisionId)},"props:get">; + type Created=CapabilityOutput<${JSON.stringify(factory.revisionId)},"create:call">; + const render=({camino,render}:{camino:Props;render:{onSelect:(note:Created)=>void;compact:boolean}})=>{render.onSelect(camino.note);return camino.title;};\n`; + try { + const file = path.join(directory, "consumer.ts"); + await fs.writeFile(file, contracts + usage); + await run(process.execPath, [compiler, "--ignoreConfig", "--noEmit", "--strict", "--target", "es2023", file]); + await fs.writeFile(file, contracts + usage + "const wrong=(props:Props)=>props.count;"); + await assert.rejects( + run(process.execPath, [compiler, "--ignoreConfig", "--noEmit", "--strict", "--target", "es2023", file]), + (error) => { + assert.match(String((error as { stdout: string }).stdout), /count.*does not exist/); + return true; + }, + ); + } finally { + await fs.rm(directory, { recursive: true, force: true }); + } +}); diff --git a/test/generics.test.ts b/test/generics.test.ts new file mode 100644 index 0000000..b2e86ba --- /dev/null +++ b/test/generics.test.ts @@ -0,0 +1,601 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { compileCapabilityResourceSource, compileCapabilitySource } from "../src/capability-language/parser.js"; +import { generateTypeScriptBindings } from "../src/bindings/index.js"; +import { generateClientContracts } from "../src/bindings/client.js"; +import { + TypeSubstitution, + appliedInterfaceId, + bindTypeParameters, + canonicalTypeArgument, + instantiateInterface, + capabilityId, + isStorableType, + valueType, + computeCapabilityClosure, + compileWorkspaceRevision, + type ClosedTypeArgument, + type GenericTypeEnvironment, + type ValueTypeExpression, +} from "../src/capability-model/index.js"; + +test("unused generic definitions prove symbolic bounds and storable aliases", () => { + const source = { repository: "https://example.test/bounds.git", commit: "a".repeat(40) }; + const interfaces = new Map(); + for (const text of [ + 'interface Named id "named" revision "named@1" {}', + 'import interface Named; interface Detailed id "detailed" revision "detailed@1" requires Named {}', + 'import interface Named; interface Requires id "requires" revision "requires@1" {}', + 'interface Stored id "stored" revision "stored@1" {}', + ]) { + const result = compileCapabilityResourceSource(text, { source, environment: { interfaces } }); + assert.ok(result.ok, JSON.stringify(result.diagnostics)); + assert.equal(result.resource.kind, "interface"); + interfaces.set(result.resource.revision.displayName, result.resource.revision); + } + const compile = (body: string) => compileCapabilityResourceSource(body, { source, environment: { interfaces } }); + const bad = compile( + 'import interface Requires; interface Bad id "bad" revision "bad@1" requires Requires {}', + ); + assert.equal(bad.ok, false); + assert.match(JSON.stringify(bad.diagnostics), /does not prove/); + const good = compile( + 'import interface Detailed; import interface Requires; interface Good id "good" revision "good@1" requires Requires {}', + ); + assert.ok(good.ok, JSON.stringify(good.diagnostics)); + const alias = compile( + 'import interface Stored; type Values = list>; interface Good id "good" revision "good@1" requires Stored> {}', + ); + assert.ok(alias.ok, JSON.stringify(alias.diagnostics)); + const nonstorable = compile( + 'import interface Stored; interface Bad id "bad" revision "bad@1" requires Stored {}', + ); + assert.equal(nonstorable.ok, false); +}); + +const note = capabilityId.atom("atom:note"); +test("Self remains contextual through local aliases", () => { + const result = compileCapabilityResourceSource( + 'type Me = ref; type Mine = optional; interface Identity id "identity" revision "identity@1" {value mine id "mine" : Mine {get id "mine:get";}}', + { source: { repository: "https://example.test/self.git", commit: "a".repeat(40) } }, + ); + assert.ok(result.ok, JSON.stringify(result.diagnostics)); + if (result.resource.kind !== "interface") throw new Error("expected interface"); + assert.equal(result.resource.revision.template?.usesSelf, true); + const closed = instantiateInterface(result.resource.revision, [], { ...environment(), self: note }); + assert.deepEqual(closed.members[0].operations[0].outputType, valueType.optional(valueType.atomRef(note))); +}); +const named = capabilityId.interfaceRevision("interface:named@1"); +const environment = (arguments_: [string, ClosedTypeArgument][] = []): GenericTypeEnvironment => ({ + arguments: new Map(arguments_), + applyInterface: (id, args) => { + assert.equal(args.length, 0); + return id; + }, + implementsInterface: (target, required) => target.kind === "atom" && target.atomId === note && required === named, +}); + +test("substitution reaches nested records, lists, optionals and object references", () => { + const substitute = new TypeSubstitution( + environment([ + ["scope/value", { kind: "value", type: valueType.int64 }], + ["scope/object", { kind: "object", target: { kind: "atom", atomId: note } }], + ]), + ); + const result = substitute.value({ + kind: "record", + fields: { + values: { kind: "list", value: { kind: "optional", value: { kind: "parameter", parameterId: "scope/value" } } }, + target: { kind: "object-ref", expectation: { kind: "parameter", parameterId: "scope/object" } }, + }, + }); + assert.deepEqual(result, { + kind: "record", + fields: { + values: valueType.list(valueType.optional(valueType.int64)), + target: valueType.atomRef(note), + }, + }); + assert.equal(isStorableType(result), false); +}); + +test("Self is the exact implementing atom and cannot be unbound", () => { + const expression = { kind: "object-ref", expectation: { kind: "self" } } as const; + assert.throws(() => new TypeSubstitution(environment()).value(expression), /Self requires an implementing atom/); + assert.deepEqual(new TypeSubstitution({ ...environment(), self: note }).value(expression), valueType.atomRef(note)); +}); + +test("parameter kinds and missing parameters fail closed", () => { + const substitute = new TypeSubstitution( + environment([["T", { kind: "object", target: { kind: "atom", atomId: note } }]]), + ); + assert.throws(() => substitute.value({ kind: "parameter", parameterId: "T" }), /use ref/); + assert.throws(() => substitute.value({ kind: "parameter", parameterId: "unknown" }), /Unbound parameter/); + assert.throws( + () => bindTypeParameters([{ id: "T", name: "T", kind: "value" }], [], environment()), + /Expected 1 type arguments/, + ); + assert.throws( + () => + bindTypeParameters( + [{ id: "T", name: "T", kind: "value" }], + [{ kind: "object", target: { kind: "atom", atomId: note } }], + environment(), + ), + /Expected value, received object/, + ); +}); + +test("bounds require evidence and storable constraints inspect nested values", () => { + const parameters = [ + { id: "T", name: "T", kind: "object", implements: [{ definitionId: named, arguments: [] }] }, + ] as const; + const mutableParameters = parameters.map((p) => ({ + ...p, + implements: p.implements.map((b) => ({ ...b, arguments: [] })), + })); + assert.equal( + bindTypeParameters(mutableParameters, [{ kind: "object", target: { kind: "atom", atomId: note } }], environment()) + .size, + 1, + ); + assert.throws( + () => + bindTypeParameters( + mutableParameters, + [{ kind: "object", target: { kind: "atom", atomId: capabilityId.atom("person") } }], + environment(), + ), + /does not implement/, + ); + assert.throws( + () => + bindTypeParameters( + [{ kind: "value", id: "V", name: "V", storable: true }], + [{ kind: "value", type: valueType.list(valueType.optional(valueType.atomRef(note))) }], + environment(), + ), + /cannot contain managed references/, + ); + assert.equal(isStorableType(valueType.message("opaque")), false); + assert.equal(isStorableType(valueType.watchHandle), false); + assert.equal( + isStorableType(valueType.message("checked"), (id) => id === "checked"), + true, + ); +}); + +test("closed applications canonicalize records without erasing nominal identity or provenance", () => { + const a: ClosedTypeArgument = { + kind: "value", + type: { kind: "record", fields: { b: valueType.int64, a: valueType.string } }, + }; + const b: ClosedTypeArgument = { + kind: "value", + type: { kind: "record", fields: { a: valueType.string, b: valueType.int64 } }, + }; + assert.equal(canonicalTypeArgument(a), canonicalTypeArgument(b)); + const application = { + definitionId: named, + source: { repository: "https://example.test/named.git", commit: "a".repeat(40) }, + arguments: [a], + }; + assert.equal(appliedInterfaceId(application), appliedInterfaceId({ ...application, arguments: [b] })); + assert.notEqual(appliedInterfaceId(application), appliedInterfaceId({ ...application, self: note })); + assert.notEqual( + appliedInterfaceId(application), + appliedInterfaceId({ ...application, source: { ...application.source, commit: "b".repeat(40) } }), + ); + assert.throws( + () => + canonicalTypeArgument({ + kind: "value", + type: { kind: "parameter", parameterId: "T" }, + } as unknown as ClosedTypeArgument), + /Expected a closed value type/, + ); +}); + +test("aliases use lexical parameter identities, preserve outer bindings and reject cycles", () => { + const env = environment([["outer/T", { kind: "value", type: valueType.string }]]); + env.aliases = new Map([ + [ + "Box", + { + id: "Box", + parameters: [{ id: "box/T", name: "T", kind: "value" }], + body: { kind: "list", value: { kind: "parameter", parameterId: "box/T" } }, + }, + ], + ["Loop", { id: "Loop", parameters: [], body: { kind: "alias", definitionId: "Loop", arguments: [] } }], + ]); + const substitute = new TypeSubstitution(env); + assert.deepEqual( + substitute.value({ + kind: "alias", + definitionId: "Box", + arguments: [{ kind: "value", type: { kind: "parameter", parameterId: "outer/T" } }], + }), + valueType.list(valueType.string), + ); + assert.deepEqual(substitute.value({ kind: "parameter", parameterId: "outer/T" }), valueType.string); + assert.throws(() => substitute.value({ kind: "alias", definitionId: "Loop", arguments: [] }), /Loop -> Loop/); +}); + +test("excessively deep types produce a bounded diagnostic", () => { + let expression: ValueTypeExpression = valueType.string; + for (let index = 0; index < 200; index++) expression = { kind: "list", value: expression }; + assert.throws(() => new TypeSubstitution(environment()).value(expression), /depth or expansion budget/); +}); + +const source = { repository: "https://example.test/contracts.git", commit: "a".repeat(40) }; +const reader = () => { + const result = compileCapabilityResourceSource( + `interface Reader id "reader" revision "reader@1" { + value values id "values" : list> { get id "values:get"; watch start id "values:watch" stop id "values:stop"; } + }`, + { source }, + ); + assert.ok(result.ok, JSON.stringify(result.diagnostics)); + assert.equal(result.resource.kind, "interface"); + if (result.resource.kind !== "interface") throw new Error("expected interface"); + return result.resource.revision; +}; + +test("generic interface source retains its template and produces closed package/codegen contracts", () => { + const definition = reader(); + assert.equal(definition.template?.parameters[0].name, "V"); + const result = compileCapabilityResourceSource( + `import interface Reader; + package Client id "client" revision "client@1" { + function run id "run" : unit -> list> requires { interface reader id "reader-port" : Reader; }; + }`, + { source, environment: { interfaces: new Map([["Reader", definition]]) } }, + ); + assert.ok(result.ok, JSON.stringify(result.diagnostics)); + if (result.resource.kind !== "package") throw new Error("expected package"); + assert.equal(result.resource.specializations?.length, 1); + const closed = result.resource.specializations![0]; + assert.deepEqual(closed.members[0].operations[0].outputType, valueType.list(valueType.optional(valueType.string))); + assert.deepEqual(closed.members[0].operations[1].eventType, closed.members[0].operations[0].outputType); + const generated = generateTypeScriptBindings( + { format: "quixos-bindings", version: 1, interfaces: [closed], packages: [result.resource.revision] }, + "client@1", + ); + assert.match(generated, /Array<\(string \| null\)>/); + assert.ok(generated.includes(closed.revisionId)); +}); + +test("generic conformances bind state against specialized signatures", () => { + const result = compileCapabilitySource( + `workspace Demo id "ws" revision "ws@1" commit "${source.commit}" { + atom Document id "document"; + import interface Reader; + conform Document as Reader id "reader-conformance" { + private state Values id "values-slot" on Document : list> policy optimistic-register; + bind values.get to state Values.read; + bind values.watch-start to state Values.watch-start; + bind values.watch-stop to state Values.watch-stop; + } + }`, + "workspace.qx", + { interfaces: new Map([["Reader", reader()]]) }, + ); + assert.ok(result.ok, JSON.stringify(result.diagnostics)); + assert.equal(result.workspace.interfaceImports.length, 1); + assert.equal(result.workspace.interfaceImports[0].template, undefined); + assert.equal(result.workspace.conformances[0].interfaceRevisionId, result.workspace.interfaceImports[0].revisionId); +}); + +test("source rejects wrong generic arity and an unapplied interface", () => { + for (const input of [ + "interface-ref", + "interface-ref>", + "interface-ref>", + ]) { + const result = compileCapabilityResourceSource( + `import interface Reader; external atom Note id "note"; + package P id "p" revision "p@1" { function f id "f" : ${input} -> unit; }`, + { source, environment: { interfaces: new Map([["Reader", reader()]]) } }, + ); + assert.equal(result.ok, false, input); + assert.match(result.diagnostics[0].code, /type-arity|parameter-kind/); + } +}); + +test("package receivers and injected dependencies select exact applications", () => { + const definition = reader(); + const summary = compileCapabilityResourceSource( + `interface Summary id "summary" revision "summary@1" { + value summary id "summary-value" : list> { get id "summary:get"; } + }`, + { source }, + ); + assert.ok(summary.ok); + if (summary.resource.kind !== "interface") throw new Error("expected interface"); + const pkg = compileCapabilityResourceSource( + `import interface Reader; + package P id "p" revision "p@1" { + operation summarize id "summarize" : unit -> list> mode call receiver interfaces [Reader] + requires { interface reader id "reader-port" : Reader; }; + }`, + { source, environment: { interfaces: new Map([["Reader", definition]]) } }, + ); + assert.ok(pkg.ok, JSON.stringify(pkg.diagnostics)); + if (pkg.resource.kind !== "package") throw new Error("expected package"); + const result = compileCapabilitySource( + `workspace W id "w" revision "w@1" commit "${source.commit}" { + import interface Reader; import interface Summary; import package P; + atom Note id "note"; + conform Note as Reader id "reader-conformance" { + private state Values id "values" on Note : list> policy optimistic-register; + bind values.get to state Values.read; + bind values.watch-start to state Values.watch-start; + bind values.watch-stop to state Values.watch-stop; + } + conform Note as Summary id "summary-conformance" { + bind summary.get to package P.summarize with { reader to interface Reader; }; + } + }`, + "workspace.qx", + { + interfaces: new Map([ + ["Reader", definition], + ["Summary", summary.resource.revision], + ]), + interfaceClosure: pkg.resource.specializations, + packages: new Map([["P", pkg.resource.revision]]), + }, + ); + assert.ok(result.ok, JSON.stringify(result.diagnostics)); +}); + +test("named generic value aliases elaborate through nested lists", () => { + const result = compileCapabilityResourceSource( + `type Page = record { items: list; next: optional; }; + package P id "p" revision "p@1" { function f id "f" : unit -> Page; }`, + { source }, + ); + assert.ok(result.ok, JSON.stringify(result.diagnostics)); + if (result.resource.kind !== "package") throw new Error("expected package"); + assert.deepEqual(result.resource.revision.exports[0].outputType, { + kind: "record", + fields: { items: valueType.list(valueType.int64), next: valueType.optional(valueType.string) }, + }); +}); + +test("Self specializes to the implementing atom rather than an erased interface", () => { + const definition = compileCapabilityResourceSource( + `interface Identity id "identity" revision "identity@1" { + operation identity id "identity-member" : ref -> ref { call id "identity-call"; } + }`, + { source }, + ); + assert.ok(definition.ok, JSON.stringify(definition.diagnostics)); + if (definition.resource.kind !== "interface") throw new Error("expected interface"); + assert.equal(definition.resource.revision.template?.usesSelf, true); + const { revision } = definition.resource; + const first = instantiateInterface(revision, [], { ...environment(), self: note }); + const second = instantiateInterface(revision, [], { ...environment(), self: capabilityId.atom("other") }); + assert.deepEqual(first.members[0].operations[0].outputType, valueType.atomRef(note)); + assert.notEqual(first.revisionId, second.revisionId); +}); + +test("package Self comes from an exact receiver, never a previous export", () => { + const valid = compileCapabilityResourceSource( + `type Owned = ref; external atom Note id "note"; + package P id "p" revision "p@1" { + operation identity id "identity" : ref -> Owned mode call receiver atom Note; + }`, + { source }, + ); + assert.ok(valid.ok, JSON.stringify(valid.diagnostics)); + if (valid.resource.kind !== "package") throw new Error("expected package"); + assert.deepEqual(valid.resource.revision.exports[0].outputType, valueType.atomRef(capabilityId.atom("note"))); + const invalid = compileCapabilityResourceSource( + `external atom Note id "note"; + package P id "p" revision "p@1" { + operation identity id "identity" : ref -> ref mode call receiver atom Note; + function bad id "bad" : unit -> ref; + }`, + { source }, + ); + assert.equal(invalid.ok, false); + assert.equal(invalid.diagnostics[0].code, "unbound-self"); +}); + +test("host generation rejects unresolved definitions and operation-only dispatch ambiguity", () => { + const definition = reader(); + assert.throws(() => generateClientContracts([definition], {}), /closed interface/); + const first = instantiateInterface(definition, [{ kind: "value", type: valueType.string }], environment()); + const second = instantiateInterface(definition, [{ kind: "value", type: valueType.int32 }], environment()); + assert.throws(() => generateClientContracts([first, second], {}), /ambiguous/); +}); + +test("finite recursive generic interface references share the same closed application", () => { + const definition = compileCapabilityResourceSource( + `interface Node id "node" revision "node@1" { + value next id "next" : optional>> { get id "next:get"; } + }`, + { source }, + ); + assert.ok(definition.ok, JSON.stringify(definition.diagnostics)); + if (definition.resource.kind !== "interface") throw new Error("expected interface"); + const result = compileCapabilityResourceSource( + `import interface Node; + package P id "p" revision "p@1" { function f id "f" : interface-ref> -> unit; } + `, + { source, environment: { interfaces: new Map([["Node", definition.resource.revision]]) } }, + ); + assert.ok(result.ok, JSON.stringify(result.diagnostics)); + const [closed] = result.resource.specializations!; + assert.equal(result.resource.specializations!.length, 1); + assert.deepEqual( + closed.members[0].operations[0].outputType, + valueType.optional(valueType.interfaceRef(closed.revisionId)), + ); +}); + +test("expanding recursive generic interfaces fail with a bounded diagnostic", () => { + const definition = compileCapabilityResourceSource( + `interface Node id "node" revision "node@1" { + value next id "next" : interface-ref>> { get id "next:get"; } + }`, + { source }, + ); + assert.ok(definition.ok, JSON.stringify(definition.diagnostics)); + if (definition.resource.kind !== "interface") throw new Error("expected interface"); + const result = compileCapabilityResourceSource( + `import interface Node; + package P id "p" revision "p@1" { function f id "f" : interface-ref> -> unit; } + `, + { source, environment: { interfaces: new Map([["Node", definition.resource.revision]]) } }, + ); + assert.equal(result.ok, false); + assert.equal(result.diagnostics[0].code, "recursive-application"); +}); + +test("storable parameters do not imply support for RPC-only records", () => { + assert.equal(isStorableType({ kind: "record", fields: { name: valueType.string } }), false); + assert.equal(isStorableType(valueType.list(valueType.optional(valueType.string))), true); +}); + +test("generic declarations reject duplicate members before specialization", () => { + const result = compileCapabilityResourceSource( + `interface Bad id "bad" revision "bad@1" { + value first id "duplicate" : V { get id "first:get"; } + value second id "duplicate" : V { get id "second:get"; } + }`, + { source }, + ); + assert.equal(result.ok, false); + assert.equal(result.diagnostics[0].code, "duplicate-interface-member"); +}); + +test("unused templates still check nested application arity and alias cycles", () => { + const malformed = compileCapabilityResourceSource( + `import interface Reader; + interface Bad id "bad" revision "bad@1" { + value item id "item" : interface-ref> { get id "get"; } + }`, + { source, environment: { interfaces: new Map([["Reader", reader()]]) } }, + ); + assert.equal(malformed.ok, false); + assert.equal(malformed.diagnostics[0].code, "type-arity"); + const cycle = compileCapabilityResourceSource( + `type Loop = list>; + interface Bad id "bad" revision "bad@1" { + value item id "item" : Loop { get id "get"; } + }`, + { source }, + ); + assert.equal(cycle.ok, false); + assert.equal(cycle.diagnostics[0].code, "recursive-alias"); +}); + +test("Self in a prerequisite contributes to the outer application identity", () => { + const identity = compileCapabilityResourceSource( + `interface Identity id "identity" revision "identity@1" { + value self id "self" : ref { get id "self:get"; } + }`, + { source }, + ); + assert.ok(identity.ok); + if (identity.resource.kind !== "interface") throw new Error("expected interface"); + const outer = compileCapabilityResourceSource( + `import interface Identity; + interface Outer id "outer" revision "outer@1" requires Identity {}`, + { source, environment: { interfaces: new Map([["Identity", identity.resource.revision]]) } }, + ); + assert.ok(outer.ok, JSON.stringify(outer.diagnostics)); + if (outer.resource.kind !== "interface") throw new Error("expected interface"); + assert.equal(outer.resource.revision.template?.usesSelf, true); +}); + +test("object bounds are discharged against the complete candidate, not source order", () => { + const namedResult = compileCapabilityResourceSource(`interface Named id "named" revision "named@1" {}`, { source }); + assert.ok(namedResult.ok); + if (namedResult.resource.kind !== "interface") throw new Error("expected interface"); + const named = namedResult.resource.revision; + const bounded = compileCapabilityResourceSource( + `import interface Named; + interface Container id "container" revision "container@1" {}`, + { source, environment: { interfaces: new Map([["Named", named]]) } }, + ); + assert.ok(bounded.ok, JSON.stringify(bounded.diagnostics)); + if (bounded.resource.kind !== "interface") throw new Error("expected interface"); + const container = bounded.resource.revision; + const compile = (evidence: string) => + compileCapabilitySource( + `workspace W id "w" revision "w@1" commit "${source.commit}" { + import interface Named; import interface Container; + atom Note id "note"; atom Index id "index"; + conform Index as Container id "index-container" {} + ${evidence} + }`, + "workspace.qx", + { + interfaces: new Map([ + ["Named", named], + ["Container", container], + ]), + }, + ); + const missing = compile(""); + assert.equal(missing.ok, false); + assert.ok(missing.diagnostics.some((issue) => issue.code === "unsatisfied-interface")); + const valid = compile('conform Note as Named id "note-named" {}'); + assert.ok(valid.ok, JSON.stringify(valid.diagnostics)); +}); + +test("prerequisites require explicit conformances and remain in the capability closure", () => { + const baseResult = compileCapabilityResourceSource(`interface Base id "base" revision "base@1" {}`, { source }); + assert.ok(baseResult.ok); + if (baseResult.resource.kind !== "interface") throw new Error("expected interface"); + const base = baseResult.resource.revision; + const derivedResult = compileCapabilityResourceSource( + `import interface Base; + interface Derived id "derived" revision "derived@1" requires Base {}`, + { + source, + environment: { interfaces: new Map([["Base", base]]) }, + }, + ); + assert.ok(derivedResult.ok, JSON.stringify(derivedResult.diagnostics)); + if (derivedResult.resource.kind !== "interface") throw new Error("expected interface"); + const derived = derivedResult.resource.revision; + const compile = (evidence: string) => + compileCapabilitySource( + `workspace W id "w" revision "w@1" commit "${source.commit}" { + import interface Base; import interface Derived; atom Note id "note"; + conform Note as Derived id "derived-conformance" {} + ${evidence} + }`, + "workspace.qx", + { + interfaces: new Map([ + ["Base", base], + ["Derived", derived], + ]), + }, + ); + assert.equal(compile("").ok, false); + const result = compile('conform Note as Base id "base-conformance" {}'); + assert.ok(result.ok, JSON.stringify(result.diagnostics)); + const root = result.workspace.conformances[0]; + const closure = computeCapabilityClosure(result.plan, [root]); + assert.equal(closure.conformances.length, 2); + const unresolved = structuredClone(result.workspace); + unresolved.interfaceImports[0].members.push({ + kind: "value", + id: capabilityId.member("unresolved"), + displayName: "unresolved", + operations: [], + valueType: { kind: "parameter", parameterId: "T" } as unknown as typeof valueType.string, + }); + assert.equal(compileWorkspaceRevision(unresolved).ok, false); + const forged = structuredClone(result.workspace); + const applied = forged.interfaceImports.find((entry) => entry.application)!; + applied.application!.arguments = [{ kind: "value", type: valueType.int64 }]; + assert.equal(compileWorkspaceRevision(forged).ok, false); +}); From 59735c5e38da6eb430c92f86870c9a94eac68791 Mon Sep 17 00:00:00 2001 From: "Timothy J. Aveni" Date: Wed, 16 Sep 2026 11:25:20 -0700 Subject: [PATCH 11/11] Add checked React field bindings and Web Studio factories Replace opaque props with checked generic presentation contracts and lazy typed interface references. Generate readonly/writable field APIs and component checks. Preserve CRDT editing through explicit resolved getter/setter contracts, binding- fenced delta RPCs, native watches and replica-aware field adapters. Custom setters retain semantic writes; storage snapshots never grant write authority. Cover concurrent edits, lost acknowledgements, readonly contracts and authorization. Add receiver-free static factory dispatch, state-field binding shorthand, and conformance-based creation. Migrate TODO, editable scaffolds and authoring guides. Verify language/codegen, SDK, RPC, browser lifecycle, local scaffolds, production browser bundling and CRDT persistence with temporary PostgreSQL. --- grammar/QuixosCapability.g4 | 8 +- proto/quixos/orch.proto | 47 +- src/bindings/cli.ts | 10 +- src/bindings/generics.ts | 16 +- src/bindings/index.ts | 9 +- src/bindings/react-platform.ts | 35 +- src/bindings/react.ts | 87 + .../generated/QuixosCapability.interp | 5 +- .../generated/QuixosCapability.tokens | 364 +-- .../generated/QuixosCapabilityLexer.interp | 5 +- .../generated/QuixosCapabilityLexer.tokens | 364 +-- .../generated/QuixosCapabilityLexer.ts | 1046 +++---- .../generated/QuixosCapabilityParser.ts | 2756 +++++++++-------- .../generated/QuixosCapabilityVisitor.ts | 7 + src/capability-language/parser.ts | 47 +- src/capability-language/scaffold-recipes.ts | 24 +- src/capability-language/tool-cli.ts | 3 + src/capability-model/types.ts | 2 + src/capability-model/validation.ts | 66 +- src/gen/quixos/orch_pb.ts | 238 +- test/react-fields.test.ts | 187 ++ test/scaffold-recipes.test.ts | 8 +- 22 files changed, 3034 insertions(+), 2300 deletions(-) create mode 100644 src/bindings/react.ts create mode 100644 test/react-fields.test.ts diff --git a/grammar/QuixosCapability.g4 b/grammar/QuixosCapability.g4 index b1825e4..38d4512 100644 --- a/grammar/QuixosCapability.g4 +++ b/grammar/QuixosCapability.g4 @@ -95,7 +95,7 @@ interfaceMember ; operationMember - : OPERATION identifier ID stringLiteral COLON valueType ARROW valueType + : STATIC? OPERATION identifier ID stringLiteral COLON valueType ARROW valueType LBRACE CALL ID stringLiteral SEMI RBRACE ; @@ -239,9 +239,14 @@ conformanceDecl conformanceItem : PRIVATE attachmentDecl | operationBindingDecl + | stateFieldBindingDecl | relationshipMaterializationDecl ; +stateFieldBindingDecl + : BIND identifier TO STATE identifier SEMI + ; + relationshipMaterializationDecl : MATERIALIZE identifier IF ABSENT USING CONSTRUCTOR identifier VIA EDGE identifier DOT identifier SEMI ; @@ -395,6 +400,7 @@ INPUT: 'input'; CONFORM: 'conform'; AS: 'as'; BIND: 'bind'; +STATIC: 'static'; TO: 'to'; PRIVATE: 'private'; SHARED: 'shared'; diff --git a/proto/quixos/orch.proto b/proto/quixos/orch.proto index bf306c2..cabf65c 100644 --- a/proto/quixos/orch.proto +++ b/proto/quixos/orch.proto @@ -9,6 +9,8 @@ import "quixos/runtime.proto"; service OrchestratorRuntime { rpc InvokeCapability(InvokeCapabilityRequest) returns (InvokeCapabilityResponse); + rpc EditCapabilityField(EditCapabilityFieldRequest) returns (InvokeCapabilityResponse); + rpc InvokeClassCapability(InvokeClassCapabilityRequest) returns (InvokeCapabilityResponse); rpc WatchCapability(WatchCapabilityRequest) returns (stream WatchCapabilityEvent); rpc ConstructObject(ConstructObjectRequest) returns (ConstructObjectResponse); rpc ResolveOrConstructRelatedObject(ResolveOrConstructRelatedObjectRequest) @@ -46,6 +48,11 @@ message InvokeCapabilityRequest { // layers so a live-value controller can recognize its own confirmation. string client_mutation_id = 4; } +message InvokeClassCapabilityRequest { + string conformance_id = 1; + string operation_id = 2; + map input = 3; +} message InvokeCapabilityResponse { string invocation_id = 1; Activation activation = 2; @@ -53,6 +60,24 @@ message InvokeCapabilityResponse { camino.Value result = 4; string error = 5; repeated quixos.runtime.DerivedDependency dependencies = 6; + FieldEditing field_editing = 7; +} + +// Resolved from the checked native getter/setter binding, not Value.source. +message FieldEditing { + string getter_operation_id = 1; + string setter_operation_id = 2; + string document_type = 3; + string binding_digest = 4; +} +message EditCapabilityFieldRequest { + // The public getter; setter must belong to the same value member. + quixos.CapabilityRef capability = 1; + string object_id = 2; + string setter_operation_id = 3; + string binding_digest = 4; + camino.CrdtValue update = 5; + string client_mutation_id = 6; } message WatchCapabilityRequest { @@ -68,18 +93,34 @@ message WatchCapabilityEvent { repeated quixos.runtime.DerivedDependency dependencies = 5; string error = 6; bool initial = 7; + FieldEditing field_editing = 8; } -message GetWorkspaceRequest {} +message GetWorkspaceRequest { + // Revision polling must not download the entire interface graph. + bool include_interface_contracts = 1; +} message GetWorkspaceResponse { string workspace_id = 1; string workspace_revision_id = 2; string source_root_commit = 3; - // Checked constructors whose wire input can be empty. Web Studio intersects - // this with its temporary Createable marker; the marker is not a factory. + // Checked constructors whose wire input can be empty. The create panel uses + // class factory conformances instead of this constructor inventory. repeated string empty_input_constructible_atom_ids = 4; repeated CapabilityInputContract capability_inputs = 5; repeated ConstructorInputContract constructor_inputs = 6; + // Exact closed interface contracts used by checked presentation consumers. + string interfaces_json = 7; + repeated ClassCapability class_capabilities = 8; +} +message ClassCapability { + string conformance_id = 1; + string atom_id = 2; + string interface_revision_id = 3; + string definition_id = 4; + string operation_id = 5; + string input_type_json = 6; + string output_type_json = 7; } message CapabilityInputContract { string interface_revision_id = 1; diff --git a/src/bindings/cli.ts b/src/bindings/cli.ts index df81397..d62f844 100644 --- a/src/bindings/cli.ts +++ b/src/bindings/cli.ts @@ -3,16 +3,22 @@ import { readFile, writeFile } from "node:fs/promises"; import { generateTypeScriptBindings } from "./index.js"; import path from "node:path"; import { reactPlatformTypes } from "./react-platform.js"; +import { generateReactBindings } from "./react.js"; const main = async () => { const [schema, revision, output, options, ...rest] = process.argv.slice(2); if (!schema || !revision || !output || rest.length) throw new Error("usage: quixos-codegen-ts SCHEMA.json PACKAGE_REVISION OUTPUT.ts [OPTIONS.json]"); const config = options ? JSON.parse(await readFile(options, "utf8")) : {}; - const generated = generateTypeScriptBindings(JSON.parse(await readFile(schema, "utf8")), revision, config); + const contracts = JSON.parse(await readFile(schema, "utf8")); + const generated = generateTypeScriptBindings(contracts, revision, config); await writeFile(output, generated); - if (config.messages?.["org.quixos.web-studio.ReactProps"]) { + if (config.react) { await writeFile(path.join(path.dirname(output), "web-studio-react-runtime.d.ts"), reactPlatformTypes); + await writeFile( + path.join(path.dirname(output), "react-props.gen.ts"), + generateReactBindings(contracts, revision, config.react.propsExports, config.react.components), + ); } }; main().catch((error: unknown) => { diff --git a/src/bindings/generics.ts b/src/bindings/generics.ts index a6c3011..f9d8f1a 100644 --- a/src/bindings/generics.ts +++ b/src/bindings/generics.ts @@ -124,7 +124,7 @@ export const genericImplementationType = ( }; const operations = (contract.template?.members ?? contract.members).flatMap((member) => member.operations - .filter((op) => op.mode === "call") + .filter((op) => op.mode === "call" && op.scope !== "class") .map((op) => ({ ...op, name: `${member.displayName}.${op.displayName}` })), ); return object([ @@ -146,11 +146,21 @@ export const genericImplementationType = ( .join(","); const receiver = definition.receiverRequirement; const context = object([ - ["objectId", `QxObjectRef<${receiver.kind === "target" ? target(receiver.target, rootScope()) : "string"}>`], + ...(definition.kind === "function" + ? [] + : [ + [ + "objectId", + `QxObjectRef<${receiver.kind === "target" ? target(receiver.target, rootScope()) : "string"}>`, + ] as [string, string], + ]), ["input", value(definition.inputType, rootScope())], ["ports", object(definition.dependencyPorts.map((entry) => [entry.displayName, port(entry)]))], ]); - const contextWithLifecycle = `${context} & QxContextLifecycle<${context} & {signal?: AbortSignal}>`; + const contextWithLifecycle = + definition.kind === "function" + ? `${context} & {signal?: AbortSignal}` + : `${context} & QxContextLifecycle<${context} & {signal?: AbortSignal}>`; const result = value(definition.eventType ?? definition.outputType, rootScope()); const handler = `<${declarations}>(context:${contextWithLifecycle})=>${result}|Promise<${result}>`; const derived = `{kind:"derived";get:${handler}}`; diff --git a/src/bindings/index.ts b/src/bindings/index.ts index 27210b3..5043659 100644 --- a/src/bindings/index.ts +++ b/src/bindings/index.ts @@ -180,7 +180,7 @@ export const generateTypeScriptBindings = ( // Streaming ports need a future streaming ABI; ordinary calls are fully typed today. const operations = contract.members.flatMap((member) => member.operations - .filter((operation) => operation.mode === "call") + .filter((operation) => operation.mode === "call" && operation.scope !== "class") .map((operation) => ({ ...operation, name: `${member.displayName}.${operation.displayName}` })), ); return { @@ -245,13 +245,15 @@ export const generateTypeScriptBindings = ( ? `QxObjectRef<${entry.receiverRequirement.interfaceRevisionIds.map((id) => q(`interface:${id}`)).join(" | ") || "never"}>` : "QxObjectRef"; const contextShape = object([ - ["objectId", receiver], + ...(entry.kind === "function" ? [] : [["objectId", receiver] as [string, string]]), ["input", type(entry.inputType)], ["ports", object(ports.map((port) => [port.name, port.type]))], ]); contexts.push([ entry.displayName, - `${contextShape} & QxContextLifecycle<${contextShape} & {signal?: AbortSignal}>`, + entry.kind === "function" + ? `${contextShape} & {signal?: AbortSignal}` + : `${contextShape} & QxContextLifecycle<${contextShape} & {signal?: AbortSignal}>`, ]); const event = entry.kind === "operation" ? entry.eventType : undefined; const contextType = `Contexts[${q(entry.displayName)}]`; @@ -266,6 +268,7 @@ export const generateTypeScriptBindings = ( : `QxHandler<${contextType}, ${outputType}>${entry.kind === "operation" && entry.mode === "call" ? ` | QxDerived<${contextType}, ${outputType}>` : ""}`, ]); specs[entry.displayName] = { + ...(entry.kind === "function" ? { receiver: "none" } : {}), inputType: entry.inputType, outputType: entry.outputType, ...(event ? { eventType: event } : {}), diff --git a/src/bindings/react-platform.ts b/src/bindings/react-platform.ts index 09e8977..d674291 100644 --- a/src/bindings/react-platform.ts +++ b/src/bindings/react-platform.ts @@ -8,21 +8,13 @@ declare module "@quixos/web-studio-react-runtime" { export type ObjectRef = string & { readonly $quixosAtom: AtomId; }; - export type LiveFieldProp = { - value: T; - source: { - objectId: string; - slotId: string; - valueType?: string; - storagePolicy?: string; - revision?: string | number | bigint; - crdtSnapshot?: { - type: string; - encoding: string; - payload: string; - }; - }; - }; + export type FieldCapability = {objectId: string; interfaceRevisionId: string; getOperationId: string; watchOperationId?: string; setOperationId?: string; inputKind?: "fields" | "value"}; + export type ReadableField = {readonly value?: T; readonly capability: FieldCapability}; + /** set(T) uses the resolved capability: native CRDT fields send incremental edits; + * custom/manual setters receive semantic values. Storage provenance grants no authority. */ + export type WritableField = ReadableField & {readonly $writeType?: (value: T) => T; readonly writable: true; readonly capability: FieldCapability & {setOperationId: string}}; + export type LiveFieldProp = ReadableField; + export type InterfaceReference = {readonly $quixosRef: string; readonly interfaceRevisionId: I; readonly fields: Fields}; export type ReactComponentHostProps = { onAction?: (action: Action) => void; fallback?: React.ReactNode; @@ -49,15 +41,8 @@ declare module "@quixos/web-studio-react-runtime" { options?: {clientMutationId?: string; signal?: AbortSignal}, ) => Promise; export const h: typeof React.createElement; - export const useLiveField: ( - field: LiveFieldProp, - options?: { - reconcileRegister?: (state: { - confirmed: T; - optimistic: T; - pending: boolean; - }) => T; - }, - ) => readonly [T, (value: T) => Promise]; + export function useLiveField(field: ReadableField, options: {write: (value: T) => Promise}): readonly [T, (value: T) => Promise]; + export function useLiveField(field: WritableField): readonly [T, (value: T) => Promise]; + export function useLiveField(field: ReadableField): readonly [T]; } `; diff --git a/src/bindings/react.ts b/src/bindings/react.ts new file mode 100644 index 0000000..f34cab3 --- /dev/null +++ b/src/bindings/react.ts @@ -0,0 +1,87 @@ +import type { BindingSchema } from "./index.js"; +import type { ValueType } from "../capability-model/types.js"; + +/** Browser projection of the SAME checked RPC types, not a second props schema. */ +export function generateReactBindings( + schema: BindingSchema, + packageRevisionId: string, + propsExports: readonly string[], + components: readonly { module: string; propsExport: string }[] = [], +) { + const pkg = schema.packages.find((entry) => entry.revisionId === packageRevisionId); + if (!pkg) throw new Error(`Unknown package ${packageRevisionId}`); + const q = JSON.stringify; + const references = new Map(); + const declarations: string[] = []; + const type = (value: ValueType): string => { + switch (value.kind) { + case "builtin": + if (value.name !== "unit") throw new Error(`Unsupported React props builtin ${value.name}`); + return "null"; + case "scalar": + return { + string: "string", + bool: "boolean", + bytes: "Uint8Array", + int64: "bigint", + uint64: "bigint", + int32: "number", + uint32: "number", + double: "number", + }[value.name]; + case "optional": + return `(${type(value.value)} | null)`; + case "list": + return `Array<${type(value.value)}>`; + case "record": + return `{${Object.entries(value.fields) + .map(([name, field]) => `${q(name)}: ${type(field)}`) + .join(";")}}`; + case "message": + throw new Error(`React props require checked values, not opaque message ${value.descriptorId}`); + case "object-ref": { + if (value.expectation.kind === "atom") return `ObjectRef<${q(value.expectation.atomId)}>`; + const id = value.expectation.interfaceRevisionId; + const existing = references.get(id); + if (existing) return existing; + const name = `Interface${references.size}`; + references.set(id, name); + const iface = schema.interfaces.find((entry) => entry.revisionId === id); + if (!iface) throw new Error(`Missing React reference contract ${id}`); + const fields = iface.members.flatMap((member) => { + if (member.kind === "operation") return []; + const get = member.operations.find((op) => op.displayName === (member.kind === "value" ? "get" : "resolve")); + if (!get) return []; + const writable = member.kind === "value" && member.operations.some((op) => op.displayName === "set"); + return [`${q(member.displayName)}: ${writable ? "WritableField" : "ReadableField"}<${type(get.outputType)}>`]; + }); + declarations.push(`export type ${name} = InterfaceReference<${q(id)}, {${fields.join(";")}}> ;`); + return name; + } + } + }; + // Only exports explicitly selected as props are projected. Non-props exports + // may legitimately use opaque messages or types unrelated to the browser. + const selected = propsExports.map((name) => { + const entry = pkg.exports.find((candidate) => candidate.displayName === name); + if (!entry) throw new Error(`Unknown React props export ${name}`); + return entry; + }); + const results = selected.map( + (entry) => + `${q(entry.displayName)}: ${type(entry.kind === "operation" ? (entry.eventType ?? entry.outputType) : entry.outputType)}`, + ); + const checks = components.map((component, i) => { + if (!propsExports.includes(component.propsExport)) + throw new Error(`Component refers to unselected props export ${component.propsExport}`); + if (!component.module.startsWith(".")) + throw new Error("React component check must name a local module relative to generated bindings"); + return `type Component${i} = CheckedComponent<${q(component.propsExport)}, typeof import(${q(component.module)})["default"]>;`; + }); + return ( + `// Generated from checked QX contracts. Do not edit.\nimport type {ObjectRef, InterfaceReference, ReadableField, WritableField} from "@quixos/web-studio-react-runtime";\n${declarations.join("\n")}\nexport type ReactResults = {${results.join(";\n")}};\n` + + (checks.length + ? `type CheckedComponent void}) => unknown> = C;\n${checks.join("\n")}\n` + : "") + ); +} diff --git a/src/capability-language/generated/QuixosCapability.interp b/src/capability-language/generated/QuixosCapability.interp index ce06dea..a4830e8 100644 --- a/src/capability-language/generated/QuixosCapability.interp +++ b/src/capability-language/generated/QuixosCapability.interp @@ -23,6 +23,7 @@ null 'conform' 'as' 'bind' +'static' 'to' 'private' 'shared' @@ -143,6 +144,7 @@ INPUT CONFORM AS BIND +STATIC TO PRIVATE SHARED @@ -284,6 +286,7 @@ edgeDecl edgeEndpoint conformanceDecl conformanceItem +stateFieldBindingDecl relationshipMaterializationDecl operationBindingDecl memberOperationRef @@ -307,4 +310,4 @@ stringLiteral atn: -[4, 1, 117, 958, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 3, 0, 143, 8, 0, 1, 1, 1, 1, 1, 1, 5, 1, 148, 8, 1, 10, 1, 12, 1, 151, 9, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 5, 3, 169, 8, 3, 10, 3, 12, 3, 172, 9, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 3, 4, 183, 8, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 195, 8, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 215, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 223, 8, 9, 1, 9, 1, 9, 1, 10, 5, 10, 228, 8, 10, 10, 10, 12, 10, 231, 9, 10, 1, 10, 1, 10, 1, 10, 3, 10, 236, 8, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 5, 10, 246, 8, 10, 10, 10, 12, 10, 249, 9, 10, 3, 10, 251, 8, 10, 1, 10, 1, 10, 5, 10, 255, 8, 10, 10, 10, 12, 10, 258, 9, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 5, 11, 266, 8, 11, 10, 11, 12, 11, 269, 9, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 3, 12, 277, 8, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 5, 12, 285, 8, 12, 10, 12, 12, 12, 288, 9, 12, 3, 12, 290, 8, 12, 3, 12, 292, 8, 12, 1, 13, 1, 13, 3, 13, 296, 8, 13, 1, 14, 1, 14, 1, 14, 1, 14, 5, 14, 302, 8, 14, 10, 14, 12, 14, 305, 9, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 3, 15, 316, 8, 15, 1, 16, 1, 16, 1, 16, 3, 16, 321, 8, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 3, 17, 330, 8, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 5, 19, 355, 8, 19, 10, 19, 12, 19, 358, 9, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 3, 20, 381, 8, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 3, 21, 391, 8, 21, 1, 21, 1, 21, 5, 21, 395, 8, 21, 10, 21, 12, 21, 398, 9, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 3, 22, 426, 8, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 3, 23, 433, 8, 23, 1, 23, 1, 23, 3, 23, 437, 8, 23, 1, 24, 5, 24, 440, 8, 24, 10, 24, 12, 24, 443, 9, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 3, 24, 453, 8, 24, 1, 24, 1, 24, 5, 24, 457, 8, 24, 10, 24, 12, 24, 460, 9, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 3, 25, 467, 8, 25, 1, 26, 1, 26, 1, 26, 3, 26, 472, 8, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 3, 26, 483, 8, 26, 1, 26, 1, 26, 1, 26, 3, 26, 488, 8, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 3, 27, 495, 8, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 3, 27, 504, 8, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 3, 28, 517, 8, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 5, 31, 536, 8, 31, 10, 31, 12, 31, 539, 9, 31, 3, 31, 541, 8, 31, 1, 31, 3, 31, 544, 8, 31, 1, 32, 1, 32, 1, 32, 5, 32, 549, 8, 32, 10, 32, 12, 32, 552, 9, 32, 1, 33, 1, 33, 1, 33, 5, 33, 557, 8, 33, 10, 33, 12, 33, 560, 9, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 3, 34, 590, 8, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 3, 34, 602, 8, 34, 1, 34, 1, 34, 3, 34, 606, 8, 34, 1, 35, 1, 35, 1, 35, 1, 35, 5, 35, 612, 8, 35, 10, 35, 12, 35, 615, 9, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 3, 38, 626, 8, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 3, 39, 640, 8, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 3, 40, 650, 8, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 3, 42, 668, 8, 42, 1, 42, 1, 42, 3, 42, 672, 8, 42, 1, 42, 3, 42, 675, 8, 42, 1, 42, 1, 42, 3, 42, 679, 8, 42, 1, 42, 3, 42, 682, 8, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 3, 43, 691, 8, 43, 1, 43, 1, 43, 3, 43, 695, 8, 43, 1, 43, 1, 43, 3, 43, 699, 8, 43, 1, 43, 1, 43, 5, 43, 703, 8, 43, 10, 43, 12, 43, 706, 9, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 3, 44, 714, 8, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 3, 48, 751, 8, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 3, 49, 770, 8, 49, 1, 49, 3, 49, 773, 8, 49, 3, 49, 775, 8, 49, 1, 50, 1, 50, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 5, 52, 784, 8, 52, 10, 52, 12, 52, 787, 9, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 3, 53, 801, 8, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 3, 53, 817, 8, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 3, 53, 826, 8, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 3, 53, 834, 8, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 3, 53, 844, 8, 53, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 853, 8, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 871, 8, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 5, 55, 893, 8, 55, 10, 55, 12, 55, 896, 9, 55, 1, 55, 1, 55, 1, 55, 3, 55, 901, 8, 55, 3, 55, 903, 8, 55, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 922, 8, 59, 1, 60, 1, 60, 1, 60, 1, 60, 5, 60, 928, 8, 60, 10, 60, 12, 60, 931, 9, 60, 3, 60, 933, 8, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 1, 62, 1, 62, 5, 62, 945, 8, 62, 10, 62, 12, 62, 948, 9, 62, 3, 62, 950, 8, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 64, 1, 64, 1, 64, 0, 0, 65, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 0, 7, 1, 0, 65, 69, 2, 0, 60, 64, 66, 67, 2, 0, 60, 61, 66, 67, 2, 0, 62, 64, 66, 67, 1, 0, 85, 92, 1, 0, 72, 75, 2, 0, 39, 39, 113, 113, 1022, 0, 142, 1, 0, 0, 0, 2, 144, 1, 0, 0, 0, 4, 154, 1, 0, 0, 0, 6, 158, 1, 0, 0, 0, 8, 182, 1, 0, 0, 0, 10, 194, 1, 0, 0, 0, 12, 196, 1, 0, 0, 0, 14, 203, 1, 0, 0, 0, 16, 214, 1, 0, 0, 0, 18, 216, 1, 0, 0, 0, 20, 229, 1, 0, 0, 0, 22, 261, 1, 0, 0, 0, 24, 291, 1, 0, 0, 0, 26, 293, 1, 0, 0, 0, 28, 297, 1, 0, 0, 0, 30, 315, 1, 0, 0, 0, 32, 317, 1, 0, 0, 0, 34, 329, 1, 0, 0, 0, 36, 331, 1, 0, 0, 0, 38, 346, 1, 0, 0, 0, 40, 380, 1, 0, 0, 0, 42, 382, 1, 0, 0, 0, 44, 425, 1, 0, 0, 0, 46, 436, 1, 0, 0, 0, 48, 441, 1, 0, 0, 0, 50, 466, 1, 0, 0, 0, 52, 468, 1, 0, 0, 0, 54, 491, 1, 0, 0, 0, 56, 507, 1, 0, 0, 0, 58, 520, 1, 0, 0, 0, 60, 523, 1, 0, 0, 0, 62, 543, 1, 0, 0, 0, 64, 545, 1, 0, 0, 0, 66, 553, 1, 0, 0, 0, 68, 605, 1, 0, 0, 0, 70, 607, 1, 0, 0, 0, 72, 618, 1, 0, 0, 0, 74, 620, 1, 0, 0, 0, 76, 625, 1, 0, 0, 0, 78, 627, 1, 0, 0, 0, 80, 649, 1, 0, 0, 0, 82, 651, 1, 0, 0, 0, 84, 660, 1, 0, 0, 0, 86, 685, 1, 0, 0, 0, 88, 713, 1, 0, 0, 0, 90, 715, 1, 0, 0, 0, 92, 729, 1, 0, 0, 0, 94, 735, 1, 0, 0, 0, 96, 750, 1, 0, 0, 0, 98, 774, 1, 0, 0, 0, 100, 776, 1, 0, 0, 0, 102, 778, 1, 0, 0, 0, 104, 780, 1, 0, 0, 0, 106, 843, 1, 0, 0, 0, 108, 845, 1, 0, 0, 0, 110, 902, 1, 0, 0, 0, 112, 904, 1, 0, 0, 0, 114, 909, 1, 0, 0, 0, 116, 911, 1, 0, 0, 0, 118, 921, 1, 0, 0, 0, 120, 923, 1, 0, 0, 0, 122, 936, 1, 0, 0, 0, 124, 940, 1, 0, 0, 0, 126, 953, 1, 0, 0, 0, 128, 955, 1, 0, 0, 0, 130, 131, 3, 6, 3, 0, 131, 132, 5, 0, 0, 1, 132, 143, 1, 0, 0, 0, 133, 134, 3, 20, 10, 0, 134, 135, 5, 0, 0, 1, 135, 143, 1, 0, 0, 0, 136, 137, 3, 48, 24, 0, 137, 138, 5, 0, 0, 1, 138, 143, 1, 0, 0, 0, 139, 140, 3, 2, 1, 0, 140, 141, 5, 0, 0, 1, 141, 143, 1, 0, 0, 0, 142, 130, 1, 0, 0, 0, 142, 133, 1, 0, 0, 0, 142, 136, 1, 0, 0, 0, 142, 139, 1, 0, 0, 0, 143, 1, 1, 0, 0, 0, 144, 145, 5, 7, 0, 0, 145, 149, 5, 101, 0, 0, 146, 148, 3, 8, 4, 0, 147, 146, 1, 0, 0, 0, 148, 151, 1, 0, 0, 0, 149, 147, 1, 0, 0, 0, 149, 150, 1, 0, 0, 0, 150, 152, 1, 0, 0, 0, 151, 149, 1, 0, 0, 0, 152, 153, 5, 102, 0, 0, 153, 3, 1, 0, 0, 0, 154, 155, 5, 8, 0, 0, 155, 156, 3, 128, 64, 0, 156, 157, 5, 98, 0, 0, 157, 5, 1, 0, 0, 0, 158, 159, 5, 1, 0, 0, 159, 160, 3, 126, 63, 0, 160, 161, 5, 48, 0, 0, 161, 162, 3, 128, 64, 0, 162, 163, 5, 42, 0, 0, 163, 164, 3, 128, 64, 0, 164, 165, 5, 41, 0, 0, 165, 166, 3, 128, 64, 0, 166, 170, 5, 101, 0, 0, 167, 169, 3, 8, 4, 0, 168, 167, 1, 0, 0, 0, 169, 172, 1, 0, 0, 0, 170, 168, 1, 0, 0, 0, 170, 171, 1, 0, 0, 0, 171, 173, 1, 0, 0, 0, 172, 170, 1, 0, 0, 0, 173, 174, 5, 102, 0, 0, 174, 7, 1, 0, 0, 0, 175, 183, 3, 4, 2, 0, 176, 183, 3, 18, 9, 0, 177, 183, 3, 10, 5, 0, 178, 183, 3, 74, 37, 0, 179, 183, 3, 86, 43, 0, 180, 183, 3, 108, 54, 0, 181, 183, 3, 32, 16, 0, 182, 175, 1, 0, 0, 0, 182, 176, 1, 0, 0, 0, 182, 177, 1, 0, 0, 0, 182, 178, 1, 0, 0, 0, 182, 179, 1, 0, 0, 0, 182, 180, 1, 0, 0, 0, 182, 181, 1, 0, 0, 0, 183, 9, 1, 0, 0, 0, 184, 185, 5, 8, 0, 0, 185, 186, 5, 11, 0, 0, 186, 187, 3, 126, 63, 0, 187, 188, 5, 98, 0, 0, 188, 195, 1, 0, 0, 0, 189, 190, 5, 8, 0, 0, 190, 191, 5, 13, 0, 0, 191, 192, 3, 126, 63, 0, 192, 193, 5, 98, 0, 0, 193, 195, 1, 0, 0, 0, 194, 184, 1, 0, 0, 0, 194, 189, 1, 0, 0, 0, 195, 11, 1, 0, 0, 0, 196, 197, 5, 9, 0, 0, 197, 198, 5, 10, 0, 0, 198, 199, 3, 126, 63, 0, 199, 200, 5, 48, 0, 0, 200, 201, 3, 128, 64, 0, 201, 202, 5, 98, 0, 0, 202, 13, 1, 0, 0, 0, 203, 204, 5, 9, 0, 0, 204, 205, 5, 11, 0, 0, 205, 206, 3, 126, 63, 0, 206, 207, 5, 42, 0, 0, 207, 208, 3, 128, 64, 0, 208, 209, 5, 98, 0, 0, 209, 15, 1, 0, 0, 0, 210, 215, 3, 10, 5, 0, 211, 215, 3, 12, 6, 0, 212, 215, 3, 14, 7, 0, 213, 215, 3, 32, 16, 0, 214, 210, 1, 0, 0, 0, 214, 211, 1, 0, 0, 0, 214, 212, 1, 0, 0, 0, 214, 213, 1, 0, 0, 0, 215, 17, 1, 0, 0, 0, 216, 217, 5, 10, 0, 0, 217, 218, 3, 126, 63, 0, 218, 219, 5, 48, 0, 0, 219, 222, 3, 128, 64, 0, 220, 221, 5, 49, 0, 0, 221, 223, 3, 128, 64, 0, 222, 220, 1, 0, 0, 0, 222, 223, 1, 0, 0, 0, 223, 224, 1, 0, 0, 0, 224, 225, 5, 98, 0, 0, 225, 19, 1, 0, 0, 0, 226, 228, 3, 16, 8, 0, 227, 226, 1, 0, 0, 0, 228, 231, 1, 0, 0, 0, 229, 227, 1, 0, 0, 0, 229, 230, 1, 0, 0, 0, 230, 232, 1, 0, 0, 0, 231, 229, 1, 0, 0, 0, 232, 233, 5, 11, 0, 0, 233, 235, 3, 126, 63, 0, 234, 236, 3, 22, 11, 0, 235, 234, 1, 0, 0, 0, 235, 236, 1, 0, 0, 0, 236, 237, 1, 0, 0, 0, 237, 238, 5, 48, 0, 0, 238, 239, 3, 128, 64, 0, 239, 240, 5, 42, 0, 0, 240, 250, 3, 128, 64, 0, 241, 242, 5, 53, 0, 0, 242, 247, 3, 26, 13, 0, 243, 244, 5, 99, 0, 0, 244, 246, 3, 26, 13, 0, 245, 243, 1, 0, 0, 0, 246, 249, 1, 0, 0, 0, 247, 245, 1, 0, 0, 0, 247, 248, 1, 0, 0, 0, 248, 251, 1, 0, 0, 0, 249, 247, 1, 0, 0, 0, 250, 241, 1, 0, 0, 0, 250, 251, 1, 0, 0, 0, 251, 252, 1, 0, 0, 0, 252, 256, 5, 101, 0, 0, 253, 255, 3, 34, 17, 0, 254, 253, 1, 0, 0, 0, 255, 258, 1, 0, 0, 0, 256, 254, 1, 0, 0, 0, 256, 257, 1, 0, 0, 0, 257, 259, 1, 0, 0, 0, 258, 256, 1, 0, 0, 0, 259, 260, 5, 102, 0, 0, 260, 21, 1, 0, 0, 0, 261, 262, 5, 107, 0, 0, 262, 267, 3, 24, 12, 0, 263, 264, 5, 99, 0, 0, 264, 266, 3, 24, 12, 0, 265, 263, 1, 0, 0, 0, 266, 269, 1, 0, 0, 0, 267, 265, 1, 0, 0, 0, 267, 268, 1, 0, 0, 0, 268, 270, 1, 0, 0, 0, 269, 267, 1, 0, 0, 0, 270, 271, 5, 108, 0, 0, 271, 23, 1, 0, 0, 0, 272, 273, 5, 14, 0, 0, 273, 276, 3, 126, 63, 0, 274, 275, 5, 97, 0, 0, 275, 277, 5, 4, 0, 0, 276, 274, 1, 0, 0, 0, 276, 277, 1, 0, 0, 0, 277, 292, 1, 0, 0, 0, 278, 279, 5, 3, 0, 0, 279, 289, 3, 126, 63, 0, 280, 281, 5, 5, 0, 0, 281, 286, 3, 26, 13, 0, 282, 283, 5, 109, 0, 0, 283, 285, 3, 26, 13, 0, 284, 282, 1, 0, 0, 0, 285, 288, 1, 0, 0, 0, 286, 284, 1, 0, 0, 0, 286, 287, 1, 0, 0, 0, 287, 290, 1, 0, 0, 0, 288, 286, 1, 0, 0, 0, 289, 280, 1, 0, 0, 0, 289, 290, 1, 0, 0, 0, 290, 292, 1, 0, 0, 0, 291, 272, 1, 0, 0, 0, 291, 278, 1, 0, 0, 0, 292, 25, 1, 0, 0, 0, 293, 295, 3, 126, 63, 0, 294, 296, 3, 28, 14, 0, 295, 294, 1, 0, 0, 0, 295, 296, 1, 0, 0, 0, 296, 27, 1, 0, 0, 0, 297, 298, 5, 107, 0, 0, 298, 303, 3, 30, 15, 0, 299, 300, 5, 99, 0, 0, 300, 302, 3, 30, 15, 0, 301, 299, 1, 0, 0, 0, 302, 305, 1, 0, 0, 0, 303, 301, 1, 0, 0, 0, 303, 304, 1, 0, 0, 0, 304, 306, 1, 0, 0, 0, 305, 303, 1, 0, 0, 0, 306, 307, 5, 108, 0, 0, 307, 29, 1, 0, 0, 0, 308, 309, 5, 10, 0, 0, 309, 316, 3, 126, 63, 0, 310, 311, 5, 11, 0, 0, 311, 316, 3, 26, 13, 0, 312, 313, 5, 3, 0, 0, 313, 316, 3, 126, 63, 0, 314, 316, 3, 110, 55, 0, 315, 308, 1, 0, 0, 0, 315, 310, 1, 0, 0, 0, 315, 312, 1, 0, 0, 0, 315, 314, 1, 0, 0, 0, 316, 31, 1, 0, 0, 0, 317, 318, 5, 2, 0, 0, 318, 320, 3, 126, 63, 0, 319, 321, 3, 22, 11, 0, 320, 319, 1, 0, 0, 0, 320, 321, 1, 0, 0, 0, 321, 322, 1, 0, 0, 0, 322, 323, 5, 110, 0, 0, 323, 324, 3, 110, 55, 0, 324, 325, 5, 98, 0, 0, 325, 33, 1, 0, 0, 0, 326, 330, 3, 38, 19, 0, 327, 330, 3, 42, 21, 0, 328, 330, 3, 36, 18, 0, 329, 326, 1, 0, 0, 0, 329, 327, 1, 0, 0, 0, 329, 328, 1, 0, 0, 0, 330, 35, 1, 0, 0, 0, 331, 332, 5, 16, 0, 0, 332, 333, 3, 126, 63, 0, 333, 334, 5, 48, 0, 0, 334, 335, 3, 128, 64, 0, 335, 336, 5, 97, 0, 0, 336, 337, 3, 110, 55, 0, 337, 338, 5, 96, 0, 0, 338, 339, 3, 110, 55, 0, 339, 340, 5, 101, 0, 0, 340, 341, 5, 65, 0, 0, 341, 342, 5, 48, 0, 0, 342, 343, 3, 128, 64, 0, 343, 344, 5, 98, 0, 0, 344, 345, 5, 102, 0, 0, 345, 37, 1, 0, 0, 0, 346, 347, 5, 14, 0, 0, 347, 348, 3, 126, 63, 0, 348, 349, 5, 48, 0, 0, 349, 350, 3, 128, 64, 0, 350, 351, 5, 97, 0, 0, 351, 352, 3, 110, 55, 0, 352, 356, 5, 101, 0, 0, 353, 355, 3, 40, 20, 0, 354, 353, 1, 0, 0, 0, 355, 358, 1, 0, 0, 0, 356, 354, 1, 0, 0, 0, 356, 357, 1, 0, 0, 0, 357, 359, 1, 0, 0, 0, 358, 356, 1, 0, 0, 0, 359, 360, 5, 102, 0, 0, 360, 39, 1, 0, 0, 0, 361, 362, 5, 55, 0, 0, 362, 363, 5, 48, 0, 0, 363, 364, 3, 128, 64, 0, 364, 365, 5, 98, 0, 0, 365, 381, 1, 0, 0, 0, 366, 367, 5, 56, 0, 0, 367, 368, 5, 48, 0, 0, 368, 369, 3, 128, 64, 0, 369, 370, 5, 98, 0, 0, 370, 381, 1, 0, 0, 0, 371, 372, 5, 57, 0, 0, 372, 373, 5, 58, 0, 0, 373, 374, 5, 48, 0, 0, 374, 375, 3, 128, 64, 0, 375, 376, 5, 59, 0, 0, 376, 377, 5, 48, 0, 0, 377, 378, 3, 128, 64, 0, 378, 379, 5, 98, 0, 0, 379, 381, 1, 0, 0, 0, 380, 361, 1, 0, 0, 0, 380, 366, 1, 0, 0, 0, 380, 371, 1, 0, 0, 0, 381, 41, 1, 0, 0, 0, 382, 383, 5, 15, 0, 0, 383, 384, 3, 126, 63, 0, 384, 385, 5, 48, 0, 0, 385, 386, 3, 128, 64, 0, 386, 387, 5, 97, 0, 0, 387, 388, 3, 116, 58, 0, 388, 390, 3, 46, 23, 0, 389, 391, 5, 76, 0, 0, 390, 389, 1, 0, 0, 0, 390, 391, 1, 0, 0, 0, 391, 392, 1, 0, 0, 0, 392, 396, 5, 101, 0, 0, 393, 395, 3, 44, 22, 0, 394, 393, 1, 0, 0, 0, 395, 398, 1, 0, 0, 0, 396, 394, 1, 0, 0, 0, 396, 397, 1, 0, 0, 0, 397, 399, 1, 0, 0, 0, 398, 396, 1, 0, 0, 0, 399, 400, 5, 102, 0, 0, 400, 43, 1, 0, 0, 0, 401, 402, 5, 62, 0, 0, 402, 403, 5, 48, 0, 0, 403, 404, 3, 128, 64, 0, 404, 405, 5, 98, 0, 0, 405, 426, 1, 0, 0, 0, 406, 407, 5, 63, 0, 0, 407, 408, 5, 48, 0, 0, 408, 409, 3, 128, 64, 0, 409, 410, 5, 98, 0, 0, 410, 426, 1, 0, 0, 0, 411, 412, 5, 64, 0, 0, 412, 413, 5, 48, 0, 0, 413, 414, 3, 128, 64, 0, 414, 415, 5, 98, 0, 0, 415, 426, 1, 0, 0, 0, 416, 417, 5, 57, 0, 0, 417, 418, 5, 58, 0, 0, 418, 419, 5, 48, 0, 0, 419, 420, 3, 128, 64, 0, 420, 421, 5, 59, 0, 0, 421, 422, 5, 48, 0, 0, 422, 423, 3, 128, 64, 0, 423, 424, 5, 98, 0, 0, 424, 426, 1, 0, 0, 0, 425, 401, 1, 0, 0, 0, 425, 406, 1, 0, 0, 0, 425, 411, 1, 0, 0, 0, 425, 416, 1, 0, 0, 0, 426, 45, 1, 0, 0, 0, 427, 428, 5, 10, 0, 0, 428, 437, 3, 126, 63, 0, 429, 430, 5, 11, 0, 0, 430, 432, 3, 126, 63, 0, 431, 433, 3, 28, 14, 0, 432, 431, 1, 0, 0, 0, 432, 433, 1, 0, 0, 0, 433, 437, 1, 0, 0, 0, 434, 435, 5, 3, 0, 0, 435, 437, 3, 126, 63, 0, 436, 427, 1, 0, 0, 0, 436, 429, 1, 0, 0, 0, 436, 434, 1, 0, 0, 0, 437, 47, 1, 0, 0, 0, 438, 440, 3, 16, 8, 0, 439, 438, 1, 0, 0, 0, 440, 443, 1, 0, 0, 0, 441, 439, 1, 0, 0, 0, 441, 442, 1, 0, 0, 0, 442, 444, 1, 0, 0, 0, 443, 441, 1, 0, 0, 0, 444, 445, 5, 13, 0, 0, 445, 446, 3, 126, 63, 0, 446, 447, 5, 48, 0, 0, 447, 448, 3, 128, 64, 0, 448, 449, 5, 42, 0, 0, 449, 452, 3, 128, 64, 0, 450, 451, 5, 43, 0, 0, 451, 453, 5, 111, 0, 0, 452, 450, 1, 0, 0, 0, 452, 453, 1, 0, 0, 0, 453, 454, 1, 0, 0, 0, 454, 458, 5, 101, 0, 0, 455, 457, 3, 50, 25, 0, 456, 455, 1, 0, 0, 0, 457, 460, 1, 0, 0, 0, 458, 456, 1, 0, 0, 0, 458, 459, 1, 0, 0, 0, 459, 461, 1, 0, 0, 0, 460, 458, 1, 0, 0, 0, 461, 462, 5, 102, 0, 0, 462, 49, 1, 0, 0, 0, 463, 467, 3, 52, 26, 0, 464, 467, 3, 54, 27, 0, 465, 467, 3, 56, 28, 0, 466, 463, 1, 0, 0, 0, 466, 464, 1, 0, 0, 0, 466, 465, 1, 0, 0, 0, 467, 51, 1, 0, 0, 0, 468, 469, 5, 16, 0, 0, 469, 471, 3, 126, 63, 0, 470, 472, 3, 22, 11, 0, 471, 470, 1, 0, 0, 0, 471, 472, 1, 0, 0, 0, 472, 473, 1, 0, 0, 0, 473, 474, 5, 48, 0, 0, 474, 475, 3, 128, 64, 0, 475, 476, 5, 97, 0, 0, 476, 477, 3, 110, 55, 0, 477, 478, 5, 96, 0, 0, 478, 479, 3, 110, 55, 0, 479, 480, 5, 50, 0, 0, 480, 482, 3, 60, 30, 0, 481, 483, 3, 58, 29, 0, 482, 481, 1, 0, 0, 0, 482, 483, 1, 0, 0, 0, 483, 484, 1, 0, 0, 0, 484, 485, 5, 52, 0, 0, 485, 487, 3, 62, 31, 0, 486, 488, 3, 66, 33, 0, 487, 486, 1, 0, 0, 0, 487, 488, 1, 0, 0, 0, 488, 489, 1, 0, 0, 0, 489, 490, 5, 98, 0, 0, 490, 53, 1, 0, 0, 0, 491, 492, 5, 17, 0, 0, 492, 494, 3, 126, 63, 0, 493, 495, 3, 22, 11, 0, 494, 493, 1, 0, 0, 0, 494, 495, 1, 0, 0, 0, 495, 496, 1, 0, 0, 0, 496, 497, 5, 48, 0, 0, 497, 498, 3, 128, 64, 0, 498, 499, 5, 97, 0, 0, 499, 500, 3, 110, 55, 0, 500, 501, 5, 96, 0, 0, 501, 503, 3, 110, 55, 0, 502, 504, 3, 66, 33, 0, 503, 502, 1, 0, 0, 0, 503, 504, 1, 0, 0, 0, 504, 505, 1, 0, 0, 0, 505, 506, 5, 98, 0, 0, 506, 55, 1, 0, 0, 0, 507, 508, 5, 18, 0, 0, 508, 509, 3, 126, 63, 0, 509, 510, 5, 48, 0, 0, 510, 511, 3, 128, 64, 0, 511, 512, 5, 19, 0, 0, 512, 513, 3, 126, 63, 0, 513, 514, 5, 97, 0, 0, 514, 516, 3, 110, 55, 0, 515, 517, 3, 66, 33, 0, 516, 515, 1, 0, 0, 0, 516, 517, 1, 0, 0, 0, 517, 518, 1, 0, 0, 0, 518, 519, 5, 98, 0, 0, 519, 57, 1, 0, 0, 0, 520, 521, 5, 51, 0, 0, 521, 522, 3, 110, 55, 0, 522, 59, 1, 0, 0, 0, 523, 524, 7, 0, 0, 0, 524, 61, 1, 0, 0, 0, 525, 544, 5, 54, 0, 0, 526, 527, 5, 10, 0, 0, 527, 544, 3, 126, 63, 0, 528, 529, 5, 3, 0, 0, 529, 544, 3, 126, 63, 0, 530, 531, 5, 12, 0, 0, 531, 540, 5, 103, 0, 0, 532, 537, 3, 26, 13, 0, 533, 534, 5, 99, 0, 0, 534, 536, 3, 26, 13, 0, 535, 533, 1, 0, 0, 0, 536, 539, 1, 0, 0, 0, 537, 535, 1, 0, 0, 0, 537, 538, 1, 0, 0, 0, 538, 541, 1, 0, 0, 0, 539, 537, 1, 0, 0, 0, 540, 532, 1, 0, 0, 0, 540, 541, 1, 0, 0, 0, 541, 542, 1, 0, 0, 0, 542, 544, 5, 104, 0, 0, 543, 525, 1, 0, 0, 0, 543, 526, 1, 0, 0, 0, 543, 528, 1, 0, 0, 0, 543, 530, 1, 0, 0, 0, 544, 63, 1, 0, 0, 0, 545, 550, 3, 126, 63, 0, 546, 547, 5, 99, 0, 0, 547, 549, 3, 126, 63, 0, 548, 546, 1, 0, 0, 0, 549, 552, 1, 0, 0, 0, 550, 548, 1, 0, 0, 0, 550, 551, 1, 0, 0, 0, 551, 65, 1, 0, 0, 0, 552, 550, 1, 0, 0, 0, 553, 554, 5, 53, 0, 0, 554, 558, 5, 101, 0, 0, 555, 557, 3, 68, 34, 0, 556, 555, 1, 0, 0, 0, 557, 560, 1, 0, 0, 0, 558, 556, 1, 0, 0, 0, 558, 559, 1, 0, 0, 0, 559, 561, 1, 0, 0, 0, 560, 558, 1, 0, 0, 0, 561, 562, 5, 102, 0, 0, 562, 67, 1, 0, 0, 0, 563, 564, 5, 27, 0, 0, 564, 565, 3, 126, 63, 0, 565, 566, 5, 48, 0, 0, 566, 567, 3, 128, 64, 0, 567, 568, 5, 97, 0, 0, 568, 569, 3, 110, 55, 0, 569, 570, 3, 70, 35, 0, 570, 571, 5, 98, 0, 0, 571, 606, 1, 0, 0, 0, 572, 573, 5, 28, 0, 0, 573, 574, 3, 126, 63, 0, 574, 575, 5, 48, 0, 0, 575, 576, 3, 128, 64, 0, 576, 577, 5, 97, 0, 0, 577, 578, 3, 116, 58, 0, 578, 579, 3, 46, 23, 0, 579, 580, 3, 70, 35, 0, 580, 581, 5, 98, 0, 0, 581, 606, 1, 0, 0, 0, 582, 583, 5, 11, 0, 0, 583, 584, 3, 126, 63, 0, 584, 585, 5, 48, 0, 0, 585, 586, 3, 128, 64, 0, 586, 587, 5, 97, 0, 0, 587, 589, 3, 126, 63, 0, 588, 590, 3, 28, 14, 0, 589, 588, 1, 0, 0, 0, 589, 590, 1, 0, 0, 0, 590, 591, 1, 0, 0, 0, 591, 592, 5, 98, 0, 0, 592, 606, 1, 0, 0, 0, 593, 594, 5, 18, 0, 0, 594, 595, 3, 126, 63, 0, 595, 596, 5, 48, 0, 0, 596, 597, 3, 128, 64, 0, 597, 598, 5, 97, 0, 0, 598, 601, 3, 126, 63, 0, 599, 600, 5, 20, 0, 0, 600, 602, 3, 110, 55, 0, 601, 599, 1, 0, 0, 0, 601, 602, 1, 0, 0, 0, 602, 603, 1, 0, 0, 0, 603, 604, 5, 98, 0, 0, 604, 606, 1, 0, 0, 0, 605, 563, 1, 0, 0, 0, 605, 572, 1, 0, 0, 0, 605, 582, 1, 0, 0, 0, 605, 593, 1, 0, 0, 0, 606, 69, 1, 0, 0, 0, 607, 608, 5, 103, 0, 0, 608, 613, 3, 72, 36, 0, 609, 610, 5, 99, 0, 0, 610, 612, 3, 72, 36, 0, 611, 609, 1, 0, 0, 0, 612, 615, 1, 0, 0, 0, 613, 611, 1, 0, 0, 0, 613, 614, 1, 0, 0, 0, 614, 616, 1, 0, 0, 0, 615, 613, 1, 0, 0, 0, 616, 617, 5, 104, 0, 0, 617, 71, 1, 0, 0, 0, 618, 619, 7, 1, 0, 0, 619, 73, 1, 0, 0, 0, 620, 621, 5, 26, 0, 0, 621, 622, 3, 76, 38, 0, 622, 75, 1, 0, 0, 0, 623, 626, 3, 78, 39, 0, 624, 626, 3, 82, 41, 0, 625, 623, 1, 0, 0, 0, 625, 624, 1, 0, 0, 0, 626, 77, 1, 0, 0, 0, 627, 628, 5, 27, 0, 0, 628, 629, 3, 126, 63, 0, 629, 630, 5, 48, 0, 0, 630, 631, 3, 128, 64, 0, 631, 632, 5, 36, 0, 0, 632, 633, 3, 126, 63, 0, 633, 634, 5, 97, 0, 0, 634, 635, 3, 110, 55, 0, 635, 636, 5, 37, 0, 0, 636, 639, 3, 80, 40, 0, 637, 638, 5, 38, 0, 0, 638, 640, 3, 118, 59, 0, 639, 637, 1, 0, 0, 0, 639, 640, 1, 0, 0, 0, 640, 641, 1, 0, 0, 0, 641, 642, 5, 98, 0, 0, 642, 79, 1, 0, 0, 0, 643, 650, 5, 70, 0, 0, 644, 645, 5, 71, 0, 0, 645, 646, 5, 105, 0, 0, 646, 647, 3, 110, 55, 0, 647, 648, 5, 106, 0, 0, 648, 650, 1, 0, 0, 0, 649, 643, 1, 0, 0, 0, 649, 644, 1, 0, 0, 0, 650, 81, 1, 0, 0, 0, 651, 652, 5, 28, 0, 0, 652, 653, 3, 126, 63, 0, 653, 654, 5, 48, 0, 0, 654, 655, 3, 128, 64, 0, 655, 656, 5, 101, 0, 0, 656, 657, 3, 84, 42, 0, 657, 658, 3, 84, 42, 0, 658, 659, 5, 102, 0, 0, 659, 83, 1, 0, 0, 0, 660, 661, 3, 46, 23, 0, 661, 662, 5, 29, 0, 0, 662, 663, 3, 126, 63, 0, 663, 664, 5, 48, 0, 0, 664, 665, 3, 128, 64, 0, 665, 667, 3, 116, 58, 0, 666, 668, 5, 76, 0, 0, 667, 666, 1, 0, 0, 0, 667, 668, 1, 0, 0, 0, 668, 671, 1, 0, 0, 0, 669, 670, 5, 44, 0, 0, 670, 672, 3, 128, 64, 0, 671, 669, 1, 0, 0, 0, 671, 672, 1, 0, 0, 0, 672, 674, 1, 0, 0, 0, 673, 675, 5, 45, 0, 0, 674, 673, 1, 0, 0, 0, 674, 675, 1, 0, 0, 0, 675, 678, 1, 0, 0, 0, 676, 677, 5, 46, 0, 0, 677, 679, 3, 128, 64, 0, 678, 676, 1, 0, 0, 0, 678, 679, 1, 0, 0, 0, 679, 681, 1, 0, 0, 0, 680, 682, 5, 47, 0, 0, 681, 680, 1, 0, 0, 0, 681, 682, 1, 0, 0, 0, 682, 683, 1, 0, 0, 0, 683, 684, 5, 98, 0, 0, 684, 85, 1, 0, 0, 0, 685, 686, 5, 21, 0, 0, 686, 687, 3, 126, 63, 0, 687, 688, 5, 22, 0, 0, 688, 690, 3, 126, 63, 0, 689, 691, 3, 28, 14, 0, 690, 689, 1, 0, 0, 0, 690, 691, 1, 0, 0, 0, 691, 694, 1, 0, 0, 0, 692, 693, 5, 48, 0, 0, 693, 695, 3, 128, 64, 0, 694, 692, 1, 0, 0, 0, 694, 695, 1, 0, 0, 0, 695, 698, 1, 0, 0, 0, 696, 697, 5, 43, 0, 0, 697, 699, 5, 111, 0, 0, 698, 696, 1, 0, 0, 0, 698, 699, 1, 0, 0, 0, 699, 700, 1, 0, 0, 0, 700, 704, 5, 101, 0, 0, 701, 703, 3, 88, 44, 0, 702, 701, 1, 0, 0, 0, 703, 706, 1, 0, 0, 0, 704, 702, 1, 0, 0, 0, 704, 705, 1, 0, 0, 0, 705, 707, 1, 0, 0, 0, 706, 704, 1, 0, 0, 0, 707, 708, 5, 102, 0, 0, 708, 87, 1, 0, 0, 0, 709, 710, 5, 25, 0, 0, 710, 714, 3, 76, 38, 0, 711, 714, 3, 92, 46, 0, 712, 714, 3, 90, 45, 0, 713, 709, 1, 0, 0, 0, 713, 711, 1, 0, 0, 0, 713, 712, 1, 0, 0, 0, 714, 89, 1, 0, 0, 0, 715, 716, 5, 33, 0, 0, 716, 717, 3, 126, 63, 0, 717, 718, 5, 34, 0, 0, 718, 719, 5, 35, 0, 0, 719, 720, 5, 31, 0, 0, 720, 721, 5, 18, 0, 0, 721, 722, 3, 126, 63, 0, 722, 723, 5, 32, 0, 0, 723, 724, 5, 28, 0, 0, 724, 725, 3, 126, 63, 0, 725, 726, 5, 100, 0, 0, 726, 727, 3, 126, 63, 0, 727, 728, 5, 98, 0, 0, 728, 91, 1, 0, 0, 0, 729, 730, 5, 23, 0, 0, 730, 731, 3, 94, 47, 0, 731, 732, 5, 24, 0, 0, 732, 733, 3, 98, 49, 0, 733, 734, 5, 98, 0, 0, 734, 93, 1, 0, 0, 0, 735, 736, 3, 126, 63, 0, 736, 737, 5, 100, 0, 0, 737, 738, 3, 96, 48, 0, 738, 95, 1, 0, 0, 0, 739, 751, 3, 126, 63, 0, 740, 751, 5, 65, 0, 0, 741, 751, 5, 55, 0, 0, 742, 751, 5, 56, 0, 0, 743, 751, 5, 62, 0, 0, 744, 751, 5, 63, 0, 0, 745, 751, 5, 64, 0, 0, 746, 751, 5, 66, 0, 0, 747, 751, 5, 67, 0, 0, 748, 751, 5, 68, 0, 0, 749, 751, 5, 69, 0, 0, 750, 739, 1, 0, 0, 0, 750, 740, 1, 0, 0, 0, 750, 741, 1, 0, 0, 0, 750, 742, 1, 0, 0, 0, 750, 743, 1, 0, 0, 0, 750, 744, 1, 0, 0, 0, 750, 745, 1, 0, 0, 0, 750, 746, 1, 0, 0, 0, 750, 747, 1, 0, 0, 0, 750, 748, 1, 0, 0, 0, 750, 749, 1, 0, 0, 0, 751, 97, 1, 0, 0, 0, 752, 753, 5, 27, 0, 0, 753, 754, 3, 126, 63, 0, 754, 755, 5, 100, 0, 0, 755, 756, 3, 100, 50, 0, 756, 775, 1, 0, 0, 0, 757, 758, 5, 28, 0, 0, 758, 759, 3, 126, 63, 0, 759, 760, 5, 100, 0, 0, 760, 761, 3, 126, 63, 0, 761, 762, 5, 100, 0, 0, 762, 763, 3, 102, 51, 0, 763, 775, 1, 0, 0, 0, 764, 765, 5, 13, 0, 0, 765, 766, 3, 126, 63, 0, 766, 767, 5, 100, 0, 0, 767, 769, 3, 126, 63, 0, 768, 770, 3, 28, 14, 0, 769, 768, 1, 0, 0, 0, 769, 770, 1, 0, 0, 0, 770, 772, 1, 0, 0, 0, 771, 773, 3, 104, 52, 0, 772, 771, 1, 0, 0, 0, 772, 773, 1, 0, 0, 0, 773, 775, 1, 0, 0, 0, 774, 752, 1, 0, 0, 0, 774, 757, 1, 0, 0, 0, 774, 764, 1, 0, 0, 0, 775, 99, 1, 0, 0, 0, 776, 777, 7, 2, 0, 0, 777, 101, 1, 0, 0, 0, 778, 779, 7, 3, 0, 0, 779, 103, 1, 0, 0, 0, 780, 781, 5, 30, 0, 0, 781, 785, 5, 101, 0, 0, 782, 784, 3, 106, 53, 0, 783, 782, 1, 0, 0, 0, 784, 787, 1, 0, 0, 0, 785, 783, 1, 0, 0, 0, 785, 786, 1, 0, 0, 0, 786, 788, 1, 0, 0, 0, 787, 785, 1, 0, 0, 0, 788, 789, 5, 102, 0, 0, 789, 105, 1, 0, 0, 0, 790, 791, 3, 126, 63, 0, 791, 792, 5, 24, 0, 0, 792, 793, 5, 27, 0, 0, 793, 800, 3, 126, 63, 0, 794, 795, 5, 32, 0, 0, 795, 796, 5, 28, 0, 0, 796, 797, 3, 126, 63, 0, 797, 798, 5, 100, 0, 0, 798, 799, 3, 126, 63, 0, 799, 801, 1, 0, 0, 0, 800, 794, 1, 0, 0, 0, 800, 801, 1, 0, 0, 0, 801, 802, 1, 0, 0, 0, 802, 803, 5, 98, 0, 0, 803, 844, 1, 0, 0, 0, 804, 805, 3, 126, 63, 0, 805, 806, 5, 24, 0, 0, 806, 807, 5, 28, 0, 0, 807, 808, 3, 126, 63, 0, 808, 809, 5, 100, 0, 0, 809, 816, 3, 126, 63, 0, 810, 811, 5, 32, 0, 0, 811, 812, 5, 28, 0, 0, 812, 813, 3, 126, 63, 0, 813, 814, 5, 100, 0, 0, 814, 815, 3, 126, 63, 0, 815, 817, 1, 0, 0, 0, 816, 810, 1, 0, 0, 0, 816, 817, 1, 0, 0, 0, 817, 818, 1, 0, 0, 0, 818, 819, 5, 98, 0, 0, 819, 844, 1, 0, 0, 0, 820, 821, 3, 126, 63, 0, 821, 822, 5, 24, 0, 0, 822, 823, 5, 11, 0, 0, 823, 825, 3, 126, 63, 0, 824, 826, 3, 28, 14, 0, 825, 824, 1, 0, 0, 0, 825, 826, 1, 0, 0, 0, 826, 833, 1, 0, 0, 0, 827, 828, 5, 32, 0, 0, 828, 829, 5, 28, 0, 0, 829, 830, 3, 126, 63, 0, 830, 831, 5, 100, 0, 0, 831, 832, 3, 126, 63, 0, 832, 834, 1, 0, 0, 0, 833, 827, 1, 0, 0, 0, 833, 834, 1, 0, 0, 0, 834, 835, 1, 0, 0, 0, 835, 836, 5, 98, 0, 0, 836, 844, 1, 0, 0, 0, 837, 838, 3, 126, 63, 0, 838, 839, 5, 24, 0, 0, 839, 840, 5, 18, 0, 0, 840, 841, 3, 126, 63, 0, 841, 842, 5, 98, 0, 0, 842, 844, 1, 0, 0, 0, 843, 790, 1, 0, 0, 0, 843, 804, 1, 0, 0, 0, 843, 820, 1, 0, 0, 0, 843, 837, 1, 0, 0, 0, 844, 107, 1, 0, 0, 0, 845, 846, 5, 18, 0, 0, 846, 847, 3, 126, 63, 0, 847, 848, 5, 24, 0, 0, 848, 849, 3, 126, 63, 0, 849, 850, 5, 100, 0, 0, 850, 852, 3, 126, 63, 0, 851, 853, 3, 104, 52, 0, 852, 851, 1, 0, 0, 0, 852, 853, 1, 0, 0, 0, 853, 854, 1, 0, 0, 0, 854, 855, 5, 98, 0, 0, 855, 109, 1, 0, 0, 0, 856, 903, 3, 114, 57, 0, 857, 903, 5, 77, 0, 0, 858, 903, 5, 78, 0, 0, 859, 860, 5, 79, 0, 0, 860, 903, 3, 128, 64, 0, 861, 862, 5, 80, 0, 0, 862, 863, 5, 107, 0, 0, 863, 864, 3, 126, 63, 0, 864, 865, 5, 108, 0, 0, 865, 903, 1, 0, 0, 0, 866, 867, 5, 81, 0, 0, 867, 868, 5, 107, 0, 0, 868, 870, 3, 126, 63, 0, 869, 871, 3, 28, 14, 0, 870, 869, 1, 0, 0, 0, 870, 871, 1, 0, 0, 0, 871, 872, 1, 0, 0, 0, 872, 873, 5, 108, 0, 0, 873, 903, 1, 0, 0, 0, 874, 875, 5, 6, 0, 0, 875, 876, 5, 107, 0, 0, 876, 877, 3, 126, 63, 0, 877, 878, 5, 108, 0, 0, 878, 903, 1, 0, 0, 0, 879, 880, 5, 82, 0, 0, 880, 881, 5, 107, 0, 0, 881, 882, 3, 110, 55, 0, 882, 883, 5, 108, 0, 0, 883, 903, 1, 0, 0, 0, 884, 885, 5, 83, 0, 0, 885, 886, 5, 107, 0, 0, 886, 887, 3, 110, 55, 0, 887, 888, 5, 108, 0, 0, 888, 903, 1, 0, 0, 0, 889, 890, 5, 84, 0, 0, 890, 894, 5, 101, 0, 0, 891, 893, 3, 112, 56, 0, 892, 891, 1, 0, 0, 0, 893, 896, 1, 0, 0, 0, 894, 892, 1, 0, 0, 0, 894, 895, 1, 0, 0, 0, 895, 897, 1, 0, 0, 0, 896, 894, 1, 0, 0, 0, 897, 903, 5, 102, 0, 0, 898, 900, 3, 126, 63, 0, 899, 901, 3, 28, 14, 0, 900, 899, 1, 0, 0, 0, 900, 901, 1, 0, 0, 0, 901, 903, 1, 0, 0, 0, 902, 856, 1, 0, 0, 0, 902, 857, 1, 0, 0, 0, 902, 858, 1, 0, 0, 0, 902, 859, 1, 0, 0, 0, 902, 861, 1, 0, 0, 0, 902, 866, 1, 0, 0, 0, 902, 874, 1, 0, 0, 0, 902, 879, 1, 0, 0, 0, 902, 884, 1, 0, 0, 0, 902, 889, 1, 0, 0, 0, 902, 898, 1, 0, 0, 0, 903, 111, 1, 0, 0, 0, 904, 905, 3, 126, 63, 0, 905, 906, 5, 97, 0, 0, 906, 907, 3, 110, 55, 0, 907, 908, 5, 98, 0, 0, 908, 113, 1, 0, 0, 0, 909, 910, 7, 4, 0, 0, 910, 115, 1, 0, 0, 0, 911, 912, 7, 5, 0, 0, 912, 117, 1, 0, 0, 0, 913, 922, 3, 128, 64, 0, 914, 922, 5, 111, 0, 0, 915, 922, 5, 112, 0, 0, 916, 922, 5, 93, 0, 0, 917, 922, 5, 94, 0, 0, 918, 922, 5, 95, 0, 0, 919, 922, 3, 120, 60, 0, 920, 922, 3, 124, 62, 0, 921, 913, 1, 0, 0, 0, 921, 914, 1, 0, 0, 0, 921, 915, 1, 0, 0, 0, 921, 916, 1, 0, 0, 0, 921, 917, 1, 0, 0, 0, 921, 918, 1, 0, 0, 0, 921, 919, 1, 0, 0, 0, 921, 920, 1, 0, 0, 0, 922, 119, 1, 0, 0, 0, 923, 932, 5, 101, 0, 0, 924, 929, 3, 122, 61, 0, 925, 926, 5, 99, 0, 0, 926, 928, 3, 122, 61, 0, 927, 925, 1, 0, 0, 0, 928, 931, 1, 0, 0, 0, 929, 927, 1, 0, 0, 0, 929, 930, 1, 0, 0, 0, 930, 933, 1, 0, 0, 0, 931, 929, 1, 0, 0, 0, 932, 924, 1, 0, 0, 0, 932, 933, 1, 0, 0, 0, 933, 934, 1, 0, 0, 0, 934, 935, 5, 102, 0, 0, 935, 121, 1, 0, 0, 0, 936, 937, 3, 128, 64, 0, 937, 938, 5, 97, 0, 0, 938, 939, 3, 118, 59, 0, 939, 123, 1, 0, 0, 0, 940, 949, 5, 103, 0, 0, 941, 946, 3, 118, 59, 0, 942, 943, 5, 99, 0, 0, 943, 945, 3, 118, 59, 0, 944, 942, 1, 0, 0, 0, 945, 948, 1, 0, 0, 0, 946, 944, 1, 0, 0, 0, 946, 947, 1, 0, 0, 0, 947, 950, 1, 0, 0, 0, 948, 946, 1, 0, 0, 0, 949, 941, 1, 0, 0, 0, 949, 950, 1, 0, 0, 0, 950, 951, 1, 0, 0, 0, 951, 952, 5, 104, 0, 0, 952, 125, 1, 0, 0, 0, 953, 954, 7, 6, 0, 0, 954, 127, 1, 0, 0, 0, 955, 956, 5, 114, 0, 0, 956, 129, 1, 0, 0, 0, 81, 142, 149, 170, 182, 194, 214, 222, 229, 235, 247, 250, 256, 267, 276, 286, 289, 291, 295, 303, 315, 320, 329, 356, 380, 390, 396, 425, 432, 436, 441, 452, 458, 466, 471, 482, 487, 494, 503, 516, 537, 540, 543, 550, 558, 589, 601, 605, 613, 625, 639, 649, 667, 671, 674, 678, 681, 690, 694, 698, 704, 713, 750, 769, 772, 774, 785, 800, 816, 825, 833, 843, 852, 870, 894, 900, 902, 921, 929, 932, 946, 949] \ No newline at end of file +[4, 1, 118, 971, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 3, 0, 145, 8, 0, 1, 1, 1, 1, 1, 1, 5, 1, 150, 8, 1, 10, 1, 12, 1, 153, 9, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 5, 3, 171, 8, 3, 10, 3, 12, 3, 174, 9, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 3, 4, 185, 8, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 197, 8, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 217, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 225, 8, 9, 1, 9, 1, 9, 1, 10, 5, 10, 230, 8, 10, 10, 10, 12, 10, 233, 9, 10, 1, 10, 1, 10, 1, 10, 3, 10, 238, 8, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 5, 10, 248, 8, 10, 10, 10, 12, 10, 251, 9, 10, 3, 10, 253, 8, 10, 1, 10, 1, 10, 5, 10, 257, 8, 10, 10, 10, 12, 10, 260, 9, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 5, 11, 268, 8, 11, 10, 11, 12, 11, 271, 9, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 3, 12, 279, 8, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 5, 12, 287, 8, 12, 10, 12, 12, 12, 290, 9, 12, 3, 12, 292, 8, 12, 3, 12, 294, 8, 12, 1, 13, 1, 13, 3, 13, 298, 8, 13, 1, 14, 1, 14, 1, 14, 1, 14, 5, 14, 304, 8, 14, 10, 14, 12, 14, 307, 9, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 3, 15, 318, 8, 15, 1, 16, 1, 16, 1, 16, 3, 16, 323, 8, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 3, 17, 332, 8, 17, 1, 18, 3, 18, 335, 8, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 5, 19, 360, 8, 19, 10, 19, 12, 19, 363, 9, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 3, 20, 386, 8, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 3, 21, 396, 8, 21, 1, 21, 1, 21, 5, 21, 400, 8, 21, 10, 21, 12, 21, 403, 9, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 3, 22, 431, 8, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 3, 23, 438, 8, 23, 1, 23, 1, 23, 3, 23, 442, 8, 23, 1, 24, 5, 24, 445, 8, 24, 10, 24, 12, 24, 448, 9, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 3, 24, 458, 8, 24, 1, 24, 1, 24, 5, 24, 462, 8, 24, 10, 24, 12, 24, 465, 9, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 3, 25, 472, 8, 25, 1, 26, 1, 26, 1, 26, 3, 26, 477, 8, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 3, 26, 488, 8, 26, 1, 26, 1, 26, 1, 26, 3, 26, 493, 8, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 3, 27, 500, 8, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 3, 27, 509, 8, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 3, 28, 522, 8, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 5, 31, 541, 8, 31, 10, 31, 12, 31, 544, 9, 31, 3, 31, 546, 8, 31, 1, 31, 3, 31, 549, 8, 31, 1, 32, 1, 32, 1, 32, 5, 32, 554, 8, 32, 10, 32, 12, 32, 557, 9, 32, 1, 33, 1, 33, 1, 33, 5, 33, 562, 8, 33, 10, 33, 12, 33, 565, 9, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 3, 34, 595, 8, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 3, 34, 607, 8, 34, 1, 34, 1, 34, 3, 34, 611, 8, 34, 1, 35, 1, 35, 1, 35, 1, 35, 5, 35, 617, 8, 35, 10, 35, 12, 35, 620, 9, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 3, 38, 631, 8, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 3, 39, 645, 8, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 3, 40, 655, 8, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 3, 42, 673, 8, 42, 1, 42, 1, 42, 3, 42, 677, 8, 42, 1, 42, 3, 42, 680, 8, 42, 1, 42, 1, 42, 3, 42, 684, 8, 42, 1, 42, 3, 42, 687, 8, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 3, 43, 696, 8, 43, 1, 43, 1, 43, 3, 43, 700, 8, 43, 1, 43, 1, 43, 3, 43, 704, 8, 43, 1, 43, 1, 43, 5, 43, 708, 8, 43, 10, 43, 12, 43, 711, 9, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 3, 44, 720, 8, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 3, 49, 764, 8, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 3, 50, 783, 8, 50, 1, 50, 3, 50, 786, 8, 50, 3, 50, 788, 8, 50, 1, 51, 1, 51, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 5, 53, 797, 8, 53, 10, 53, 12, 53, 800, 9, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 814, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 830, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 839, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 847, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 857, 8, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 866, 8, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 3, 56, 884, 8, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 5, 56, 906, 8, 56, 10, 56, 12, 56, 909, 9, 56, 1, 56, 1, 56, 1, 56, 3, 56, 914, 8, 56, 3, 56, 916, 8, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 3, 60, 935, 8, 60, 1, 61, 1, 61, 1, 61, 1, 61, 5, 61, 941, 8, 61, 10, 61, 12, 61, 944, 9, 61, 3, 61, 946, 8, 61, 1, 61, 1, 61, 1, 62, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 1, 63, 5, 63, 958, 8, 63, 10, 63, 12, 63, 961, 9, 63, 3, 63, 963, 8, 63, 1, 63, 1, 63, 1, 64, 1, 64, 1, 65, 1, 65, 1, 65, 0, 0, 66, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 0, 7, 1, 0, 66, 70, 2, 0, 61, 65, 67, 68, 2, 0, 61, 62, 67, 68, 2, 0, 63, 65, 67, 68, 1, 0, 86, 93, 1, 0, 73, 76, 2, 0, 40, 40, 114, 114, 1036, 0, 144, 1, 0, 0, 0, 2, 146, 1, 0, 0, 0, 4, 156, 1, 0, 0, 0, 6, 160, 1, 0, 0, 0, 8, 184, 1, 0, 0, 0, 10, 196, 1, 0, 0, 0, 12, 198, 1, 0, 0, 0, 14, 205, 1, 0, 0, 0, 16, 216, 1, 0, 0, 0, 18, 218, 1, 0, 0, 0, 20, 231, 1, 0, 0, 0, 22, 263, 1, 0, 0, 0, 24, 293, 1, 0, 0, 0, 26, 295, 1, 0, 0, 0, 28, 299, 1, 0, 0, 0, 30, 317, 1, 0, 0, 0, 32, 319, 1, 0, 0, 0, 34, 331, 1, 0, 0, 0, 36, 334, 1, 0, 0, 0, 38, 351, 1, 0, 0, 0, 40, 385, 1, 0, 0, 0, 42, 387, 1, 0, 0, 0, 44, 430, 1, 0, 0, 0, 46, 441, 1, 0, 0, 0, 48, 446, 1, 0, 0, 0, 50, 471, 1, 0, 0, 0, 52, 473, 1, 0, 0, 0, 54, 496, 1, 0, 0, 0, 56, 512, 1, 0, 0, 0, 58, 525, 1, 0, 0, 0, 60, 528, 1, 0, 0, 0, 62, 548, 1, 0, 0, 0, 64, 550, 1, 0, 0, 0, 66, 558, 1, 0, 0, 0, 68, 610, 1, 0, 0, 0, 70, 612, 1, 0, 0, 0, 72, 623, 1, 0, 0, 0, 74, 625, 1, 0, 0, 0, 76, 630, 1, 0, 0, 0, 78, 632, 1, 0, 0, 0, 80, 654, 1, 0, 0, 0, 82, 656, 1, 0, 0, 0, 84, 665, 1, 0, 0, 0, 86, 690, 1, 0, 0, 0, 88, 719, 1, 0, 0, 0, 90, 721, 1, 0, 0, 0, 92, 728, 1, 0, 0, 0, 94, 742, 1, 0, 0, 0, 96, 748, 1, 0, 0, 0, 98, 763, 1, 0, 0, 0, 100, 787, 1, 0, 0, 0, 102, 789, 1, 0, 0, 0, 104, 791, 1, 0, 0, 0, 106, 793, 1, 0, 0, 0, 108, 856, 1, 0, 0, 0, 110, 858, 1, 0, 0, 0, 112, 915, 1, 0, 0, 0, 114, 917, 1, 0, 0, 0, 116, 922, 1, 0, 0, 0, 118, 924, 1, 0, 0, 0, 120, 934, 1, 0, 0, 0, 122, 936, 1, 0, 0, 0, 124, 949, 1, 0, 0, 0, 126, 953, 1, 0, 0, 0, 128, 966, 1, 0, 0, 0, 130, 968, 1, 0, 0, 0, 132, 133, 3, 6, 3, 0, 133, 134, 5, 0, 0, 1, 134, 145, 1, 0, 0, 0, 135, 136, 3, 20, 10, 0, 136, 137, 5, 0, 0, 1, 137, 145, 1, 0, 0, 0, 138, 139, 3, 48, 24, 0, 139, 140, 5, 0, 0, 1, 140, 145, 1, 0, 0, 0, 141, 142, 3, 2, 1, 0, 142, 143, 5, 0, 0, 1, 143, 145, 1, 0, 0, 0, 144, 132, 1, 0, 0, 0, 144, 135, 1, 0, 0, 0, 144, 138, 1, 0, 0, 0, 144, 141, 1, 0, 0, 0, 145, 1, 1, 0, 0, 0, 146, 147, 5, 7, 0, 0, 147, 151, 5, 102, 0, 0, 148, 150, 3, 8, 4, 0, 149, 148, 1, 0, 0, 0, 150, 153, 1, 0, 0, 0, 151, 149, 1, 0, 0, 0, 151, 152, 1, 0, 0, 0, 152, 154, 1, 0, 0, 0, 153, 151, 1, 0, 0, 0, 154, 155, 5, 103, 0, 0, 155, 3, 1, 0, 0, 0, 156, 157, 5, 8, 0, 0, 157, 158, 3, 130, 65, 0, 158, 159, 5, 99, 0, 0, 159, 5, 1, 0, 0, 0, 160, 161, 5, 1, 0, 0, 161, 162, 3, 128, 64, 0, 162, 163, 5, 49, 0, 0, 163, 164, 3, 130, 65, 0, 164, 165, 5, 43, 0, 0, 165, 166, 3, 130, 65, 0, 166, 167, 5, 42, 0, 0, 167, 168, 3, 130, 65, 0, 168, 172, 5, 102, 0, 0, 169, 171, 3, 8, 4, 0, 170, 169, 1, 0, 0, 0, 171, 174, 1, 0, 0, 0, 172, 170, 1, 0, 0, 0, 172, 173, 1, 0, 0, 0, 173, 175, 1, 0, 0, 0, 174, 172, 1, 0, 0, 0, 175, 176, 5, 103, 0, 0, 176, 7, 1, 0, 0, 0, 177, 185, 3, 4, 2, 0, 178, 185, 3, 18, 9, 0, 179, 185, 3, 10, 5, 0, 180, 185, 3, 74, 37, 0, 181, 185, 3, 86, 43, 0, 182, 185, 3, 110, 55, 0, 183, 185, 3, 32, 16, 0, 184, 177, 1, 0, 0, 0, 184, 178, 1, 0, 0, 0, 184, 179, 1, 0, 0, 0, 184, 180, 1, 0, 0, 0, 184, 181, 1, 0, 0, 0, 184, 182, 1, 0, 0, 0, 184, 183, 1, 0, 0, 0, 185, 9, 1, 0, 0, 0, 186, 187, 5, 8, 0, 0, 187, 188, 5, 11, 0, 0, 188, 189, 3, 128, 64, 0, 189, 190, 5, 99, 0, 0, 190, 197, 1, 0, 0, 0, 191, 192, 5, 8, 0, 0, 192, 193, 5, 13, 0, 0, 193, 194, 3, 128, 64, 0, 194, 195, 5, 99, 0, 0, 195, 197, 1, 0, 0, 0, 196, 186, 1, 0, 0, 0, 196, 191, 1, 0, 0, 0, 197, 11, 1, 0, 0, 0, 198, 199, 5, 9, 0, 0, 199, 200, 5, 10, 0, 0, 200, 201, 3, 128, 64, 0, 201, 202, 5, 49, 0, 0, 202, 203, 3, 130, 65, 0, 203, 204, 5, 99, 0, 0, 204, 13, 1, 0, 0, 0, 205, 206, 5, 9, 0, 0, 206, 207, 5, 11, 0, 0, 207, 208, 3, 128, 64, 0, 208, 209, 5, 43, 0, 0, 209, 210, 3, 130, 65, 0, 210, 211, 5, 99, 0, 0, 211, 15, 1, 0, 0, 0, 212, 217, 3, 10, 5, 0, 213, 217, 3, 12, 6, 0, 214, 217, 3, 14, 7, 0, 215, 217, 3, 32, 16, 0, 216, 212, 1, 0, 0, 0, 216, 213, 1, 0, 0, 0, 216, 214, 1, 0, 0, 0, 216, 215, 1, 0, 0, 0, 217, 17, 1, 0, 0, 0, 218, 219, 5, 10, 0, 0, 219, 220, 3, 128, 64, 0, 220, 221, 5, 49, 0, 0, 221, 224, 3, 130, 65, 0, 222, 223, 5, 50, 0, 0, 223, 225, 3, 130, 65, 0, 224, 222, 1, 0, 0, 0, 224, 225, 1, 0, 0, 0, 225, 226, 1, 0, 0, 0, 226, 227, 5, 99, 0, 0, 227, 19, 1, 0, 0, 0, 228, 230, 3, 16, 8, 0, 229, 228, 1, 0, 0, 0, 230, 233, 1, 0, 0, 0, 231, 229, 1, 0, 0, 0, 231, 232, 1, 0, 0, 0, 232, 234, 1, 0, 0, 0, 233, 231, 1, 0, 0, 0, 234, 235, 5, 11, 0, 0, 235, 237, 3, 128, 64, 0, 236, 238, 3, 22, 11, 0, 237, 236, 1, 0, 0, 0, 237, 238, 1, 0, 0, 0, 238, 239, 1, 0, 0, 0, 239, 240, 5, 49, 0, 0, 240, 241, 3, 130, 65, 0, 241, 242, 5, 43, 0, 0, 242, 252, 3, 130, 65, 0, 243, 244, 5, 54, 0, 0, 244, 249, 3, 26, 13, 0, 245, 246, 5, 100, 0, 0, 246, 248, 3, 26, 13, 0, 247, 245, 1, 0, 0, 0, 248, 251, 1, 0, 0, 0, 249, 247, 1, 0, 0, 0, 249, 250, 1, 0, 0, 0, 250, 253, 1, 0, 0, 0, 251, 249, 1, 0, 0, 0, 252, 243, 1, 0, 0, 0, 252, 253, 1, 0, 0, 0, 253, 254, 1, 0, 0, 0, 254, 258, 5, 102, 0, 0, 255, 257, 3, 34, 17, 0, 256, 255, 1, 0, 0, 0, 257, 260, 1, 0, 0, 0, 258, 256, 1, 0, 0, 0, 258, 259, 1, 0, 0, 0, 259, 261, 1, 0, 0, 0, 260, 258, 1, 0, 0, 0, 261, 262, 5, 103, 0, 0, 262, 21, 1, 0, 0, 0, 263, 264, 5, 108, 0, 0, 264, 269, 3, 24, 12, 0, 265, 266, 5, 100, 0, 0, 266, 268, 3, 24, 12, 0, 267, 265, 1, 0, 0, 0, 268, 271, 1, 0, 0, 0, 269, 267, 1, 0, 0, 0, 269, 270, 1, 0, 0, 0, 270, 272, 1, 0, 0, 0, 271, 269, 1, 0, 0, 0, 272, 273, 5, 109, 0, 0, 273, 23, 1, 0, 0, 0, 274, 275, 5, 14, 0, 0, 275, 278, 3, 128, 64, 0, 276, 277, 5, 98, 0, 0, 277, 279, 5, 4, 0, 0, 278, 276, 1, 0, 0, 0, 278, 279, 1, 0, 0, 0, 279, 294, 1, 0, 0, 0, 280, 281, 5, 3, 0, 0, 281, 291, 3, 128, 64, 0, 282, 283, 5, 5, 0, 0, 283, 288, 3, 26, 13, 0, 284, 285, 5, 110, 0, 0, 285, 287, 3, 26, 13, 0, 286, 284, 1, 0, 0, 0, 287, 290, 1, 0, 0, 0, 288, 286, 1, 0, 0, 0, 288, 289, 1, 0, 0, 0, 289, 292, 1, 0, 0, 0, 290, 288, 1, 0, 0, 0, 291, 282, 1, 0, 0, 0, 291, 292, 1, 0, 0, 0, 292, 294, 1, 0, 0, 0, 293, 274, 1, 0, 0, 0, 293, 280, 1, 0, 0, 0, 294, 25, 1, 0, 0, 0, 295, 297, 3, 128, 64, 0, 296, 298, 3, 28, 14, 0, 297, 296, 1, 0, 0, 0, 297, 298, 1, 0, 0, 0, 298, 27, 1, 0, 0, 0, 299, 300, 5, 108, 0, 0, 300, 305, 3, 30, 15, 0, 301, 302, 5, 100, 0, 0, 302, 304, 3, 30, 15, 0, 303, 301, 1, 0, 0, 0, 304, 307, 1, 0, 0, 0, 305, 303, 1, 0, 0, 0, 305, 306, 1, 0, 0, 0, 306, 308, 1, 0, 0, 0, 307, 305, 1, 0, 0, 0, 308, 309, 5, 109, 0, 0, 309, 29, 1, 0, 0, 0, 310, 311, 5, 10, 0, 0, 311, 318, 3, 128, 64, 0, 312, 313, 5, 11, 0, 0, 313, 318, 3, 26, 13, 0, 314, 315, 5, 3, 0, 0, 315, 318, 3, 128, 64, 0, 316, 318, 3, 112, 56, 0, 317, 310, 1, 0, 0, 0, 317, 312, 1, 0, 0, 0, 317, 314, 1, 0, 0, 0, 317, 316, 1, 0, 0, 0, 318, 31, 1, 0, 0, 0, 319, 320, 5, 2, 0, 0, 320, 322, 3, 128, 64, 0, 321, 323, 3, 22, 11, 0, 322, 321, 1, 0, 0, 0, 322, 323, 1, 0, 0, 0, 323, 324, 1, 0, 0, 0, 324, 325, 5, 111, 0, 0, 325, 326, 3, 112, 56, 0, 326, 327, 5, 99, 0, 0, 327, 33, 1, 0, 0, 0, 328, 332, 3, 38, 19, 0, 329, 332, 3, 42, 21, 0, 330, 332, 3, 36, 18, 0, 331, 328, 1, 0, 0, 0, 331, 329, 1, 0, 0, 0, 331, 330, 1, 0, 0, 0, 332, 35, 1, 0, 0, 0, 333, 335, 5, 24, 0, 0, 334, 333, 1, 0, 0, 0, 334, 335, 1, 0, 0, 0, 335, 336, 1, 0, 0, 0, 336, 337, 5, 16, 0, 0, 337, 338, 3, 128, 64, 0, 338, 339, 5, 49, 0, 0, 339, 340, 3, 130, 65, 0, 340, 341, 5, 98, 0, 0, 341, 342, 3, 112, 56, 0, 342, 343, 5, 97, 0, 0, 343, 344, 3, 112, 56, 0, 344, 345, 5, 102, 0, 0, 345, 346, 5, 66, 0, 0, 346, 347, 5, 49, 0, 0, 347, 348, 3, 130, 65, 0, 348, 349, 5, 99, 0, 0, 349, 350, 5, 103, 0, 0, 350, 37, 1, 0, 0, 0, 351, 352, 5, 14, 0, 0, 352, 353, 3, 128, 64, 0, 353, 354, 5, 49, 0, 0, 354, 355, 3, 130, 65, 0, 355, 356, 5, 98, 0, 0, 356, 357, 3, 112, 56, 0, 357, 361, 5, 102, 0, 0, 358, 360, 3, 40, 20, 0, 359, 358, 1, 0, 0, 0, 360, 363, 1, 0, 0, 0, 361, 359, 1, 0, 0, 0, 361, 362, 1, 0, 0, 0, 362, 364, 1, 0, 0, 0, 363, 361, 1, 0, 0, 0, 364, 365, 5, 103, 0, 0, 365, 39, 1, 0, 0, 0, 366, 367, 5, 56, 0, 0, 367, 368, 5, 49, 0, 0, 368, 369, 3, 130, 65, 0, 369, 370, 5, 99, 0, 0, 370, 386, 1, 0, 0, 0, 371, 372, 5, 57, 0, 0, 372, 373, 5, 49, 0, 0, 373, 374, 3, 130, 65, 0, 374, 375, 5, 99, 0, 0, 375, 386, 1, 0, 0, 0, 376, 377, 5, 58, 0, 0, 377, 378, 5, 59, 0, 0, 378, 379, 5, 49, 0, 0, 379, 380, 3, 130, 65, 0, 380, 381, 5, 60, 0, 0, 381, 382, 5, 49, 0, 0, 382, 383, 3, 130, 65, 0, 383, 384, 5, 99, 0, 0, 384, 386, 1, 0, 0, 0, 385, 366, 1, 0, 0, 0, 385, 371, 1, 0, 0, 0, 385, 376, 1, 0, 0, 0, 386, 41, 1, 0, 0, 0, 387, 388, 5, 15, 0, 0, 388, 389, 3, 128, 64, 0, 389, 390, 5, 49, 0, 0, 390, 391, 3, 130, 65, 0, 391, 392, 5, 98, 0, 0, 392, 393, 3, 118, 59, 0, 393, 395, 3, 46, 23, 0, 394, 396, 5, 77, 0, 0, 395, 394, 1, 0, 0, 0, 395, 396, 1, 0, 0, 0, 396, 397, 1, 0, 0, 0, 397, 401, 5, 102, 0, 0, 398, 400, 3, 44, 22, 0, 399, 398, 1, 0, 0, 0, 400, 403, 1, 0, 0, 0, 401, 399, 1, 0, 0, 0, 401, 402, 1, 0, 0, 0, 402, 404, 1, 0, 0, 0, 403, 401, 1, 0, 0, 0, 404, 405, 5, 103, 0, 0, 405, 43, 1, 0, 0, 0, 406, 407, 5, 63, 0, 0, 407, 408, 5, 49, 0, 0, 408, 409, 3, 130, 65, 0, 409, 410, 5, 99, 0, 0, 410, 431, 1, 0, 0, 0, 411, 412, 5, 64, 0, 0, 412, 413, 5, 49, 0, 0, 413, 414, 3, 130, 65, 0, 414, 415, 5, 99, 0, 0, 415, 431, 1, 0, 0, 0, 416, 417, 5, 65, 0, 0, 417, 418, 5, 49, 0, 0, 418, 419, 3, 130, 65, 0, 419, 420, 5, 99, 0, 0, 420, 431, 1, 0, 0, 0, 421, 422, 5, 58, 0, 0, 422, 423, 5, 59, 0, 0, 423, 424, 5, 49, 0, 0, 424, 425, 3, 130, 65, 0, 425, 426, 5, 60, 0, 0, 426, 427, 5, 49, 0, 0, 427, 428, 3, 130, 65, 0, 428, 429, 5, 99, 0, 0, 429, 431, 1, 0, 0, 0, 430, 406, 1, 0, 0, 0, 430, 411, 1, 0, 0, 0, 430, 416, 1, 0, 0, 0, 430, 421, 1, 0, 0, 0, 431, 45, 1, 0, 0, 0, 432, 433, 5, 10, 0, 0, 433, 442, 3, 128, 64, 0, 434, 435, 5, 11, 0, 0, 435, 437, 3, 128, 64, 0, 436, 438, 3, 28, 14, 0, 437, 436, 1, 0, 0, 0, 437, 438, 1, 0, 0, 0, 438, 442, 1, 0, 0, 0, 439, 440, 5, 3, 0, 0, 440, 442, 3, 128, 64, 0, 441, 432, 1, 0, 0, 0, 441, 434, 1, 0, 0, 0, 441, 439, 1, 0, 0, 0, 442, 47, 1, 0, 0, 0, 443, 445, 3, 16, 8, 0, 444, 443, 1, 0, 0, 0, 445, 448, 1, 0, 0, 0, 446, 444, 1, 0, 0, 0, 446, 447, 1, 0, 0, 0, 447, 449, 1, 0, 0, 0, 448, 446, 1, 0, 0, 0, 449, 450, 5, 13, 0, 0, 450, 451, 3, 128, 64, 0, 451, 452, 5, 49, 0, 0, 452, 453, 3, 130, 65, 0, 453, 454, 5, 43, 0, 0, 454, 457, 3, 130, 65, 0, 455, 456, 5, 44, 0, 0, 456, 458, 5, 112, 0, 0, 457, 455, 1, 0, 0, 0, 457, 458, 1, 0, 0, 0, 458, 459, 1, 0, 0, 0, 459, 463, 5, 102, 0, 0, 460, 462, 3, 50, 25, 0, 461, 460, 1, 0, 0, 0, 462, 465, 1, 0, 0, 0, 463, 461, 1, 0, 0, 0, 463, 464, 1, 0, 0, 0, 464, 466, 1, 0, 0, 0, 465, 463, 1, 0, 0, 0, 466, 467, 5, 103, 0, 0, 467, 49, 1, 0, 0, 0, 468, 472, 3, 52, 26, 0, 469, 472, 3, 54, 27, 0, 470, 472, 3, 56, 28, 0, 471, 468, 1, 0, 0, 0, 471, 469, 1, 0, 0, 0, 471, 470, 1, 0, 0, 0, 472, 51, 1, 0, 0, 0, 473, 474, 5, 16, 0, 0, 474, 476, 3, 128, 64, 0, 475, 477, 3, 22, 11, 0, 476, 475, 1, 0, 0, 0, 476, 477, 1, 0, 0, 0, 477, 478, 1, 0, 0, 0, 478, 479, 5, 49, 0, 0, 479, 480, 3, 130, 65, 0, 480, 481, 5, 98, 0, 0, 481, 482, 3, 112, 56, 0, 482, 483, 5, 97, 0, 0, 483, 484, 3, 112, 56, 0, 484, 485, 5, 51, 0, 0, 485, 487, 3, 60, 30, 0, 486, 488, 3, 58, 29, 0, 487, 486, 1, 0, 0, 0, 487, 488, 1, 0, 0, 0, 488, 489, 1, 0, 0, 0, 489, 490, 5, 53, 0, 0, 490, 492, 3, 62, 31, 0, 491, 493, 3, 66, 33, 0, 492, 491, 1, 0, 0, 0, 492, 493, 1, 0, 0, 0, 493, 494, 1, 0, 0, 0, 494, 495, 5, 99, 0, 0, 495, 53, 1, 0, 0, 0, 496, 497, 5, 17, 0, 0, 497, 499, 3, 128, 64, 0, 498, 500, 3, 22, 11, 0, 499, 498, 1, 0, 0, 0, 499, 500, 1, 0, 0, 0, 500, 501, 1, 0, 0, 0, 501, 502, 5, 49, 0, 0, 502, 503, 3, 130, 65, 0, 503, 504, 5, 98, 0, 0, 504, 505, 3, 112, 56, 0, 505, 506, 5, 97, 0, 0, 506, 508, 3, 112, 56, 0, 507, 509, 3, 66, 33, 0, 508, 507, 1, 0, 0, 0, 508, 509, 1, 0, 0, 0, 509, 510, 1, 0, 0, 0, 510, 511, 5, 99, 0, 0, 511, 55, 1, 0, 0, 0, 512, 513, 5, 18, 0, 0, 513, 514, 3, 128, 64, 0, 514, 515, 5, 49, 0, 0, 515, 516, 3, 130, 65, 0, 516, 517, 5, 19, 0, 0, 517, 518, 3, 128, 64, 0, 518, 519, 5, 98, 0, 0, 519, 521, 3, 112, 56, 0, 520, 522, 3, 66, 33, 0, 521, 520, 1, 0, 0, 0, 521, 522, 1, 0, 0, 0, 522, 523, 1, 0, 0, 0, 523, 524, 5, 99, 0, 0, 524, 57, 1, 0, 0, 0, 525, 526, 5, 52, 0, 0, 526, 527, 3, 112, 56, 0, 527, 59, 1, 0, 0, 0, 528, 529, 7, 0, 0, 0, 529, 61, 1, 0, 0, 0, 530, 549, 5, 55, 0, 0, 531, 532, 5, 10, 0, 0, 532, 549, 3, 128, 64, 0, 533, 534, 5, 3, 0, 0, 534, 549, 3, 128, 64, 0, 535, 536, 5, 12, 0, 0, 536, 545, 5, 104, 0, 0, 537, 542, 3, 26, 13, 0, 538, 539, 5, 100, 0, 0, 539, 541, 3, 26, 13, 0, 540, 538, 1, 0, 0, 0, 541, 544, 1, 0, 0, 0, 542, 540, 1, 0, 0, 0, 542, 543, 1, 0, 0, 0, 543, 546, 1, 0, 0, 0, 544, 542, 1, 0, 0, 0, 545, 537, 1, 0, 0, 0, 545, 546, 1, 0, 0, 0, 546, 547, 1, 0, 0, 0, 547, 549, 5, 105, 0, 0, 548, 530, 1, 0, 0, 0, 548, 531, 1, 0, 0, 0, 548, 533, 1, 0, 0, 0, 548, 535, 1, 0, 0, 0, 549, 63, 1, 0, 0, 0, 550, 555, 3, 128, 64, 0, 551, 552, 5, 100, 0, 0, 552, 554, 3, 128, 64, 0, 553, 551, 1, 0, 0, 0, 554, 557, 1, 0, 0, 0, 555, 553, 1, 0, 0, 0, 555, 556, 1, 0, 0, 0, 556, 65, 1, 0, 0, 0, 557, 555, 1, 0, 0, 0, 558, 559, 5, 54, 0, 0, 559, 563, 5, 102, 0, 0, 560, 562, 3, 68, 34, 0, 561, 560, 1, 0, 0, 0, 562, 565, 1, 0, 0, 0, 563, 561, 1, 0, 0, 0, 563, 564, 1, 0, 0, 0, 564, 566, 1, 0, 0, 0, 565, 563, 1, 0, 0, 0, 566, 567, 5, 103, 0, 0, 567, 67, 1, 0, 0, 0, 568, 569, 5, 28, 0, 0, 569, 570, 3, 128, 64, 0, 570, 571, 5, 49, 0, 0, 571, 572, 3, 130, 65, 0, 572, 573, 5, 98, 0, 0, 573, 574, 3, 112, 56, 0, 574, 575, 3, 70, 35, 0, 575, 576, 5, 99, 0, 0, 576, 611, 1, 0, 0, 0, 577, 578, 5, 29, 0, 0, 578, 579, 3, 128, 64, 0, 579, 580, 5, 49, 0, 0, 580, 581, 3, 130, 65, 0, 581, 582, 5, 98, 0, 0, 582, 583, 3, 118, 59, 0, 583, 584, 3, 46, 23, 0, 584, 585, 3, 70, 35, 0, 585, 586, 5, 99, 0, 0, 586, 611, 1, 0, 0, 0, 587, 588, 5, 11, 0, 0, 588, 589, 3, 128, 64, 0, 589, 590, 5, 49, 0, 0, 590, 591, 3, 130, 65, 0, 591, 592, 5, 98, 0, 0, 592, 594, 3, 128, 64, 0, 593, 595, 3, 28, 14, 0, 594, 593, 1, 0, 0, 0, 594, 595, 1, 0, 0, 0, 595, 596, 1, 0, 0, 0, 596, 597, 5, 99, 0, 0, 597, 611, 1, 0, 0, 0, 598, 599, 5, 18, 0, 0, 599, 600, 3, 128, 64, 0, 600, 601, 5, 49, 0, 0, 601, 602, 3, 130, 65, 0, 602, 603, 5, 98, 0, 0, 603, 606, 3, 128, 64, 0, 604, 605, 5, 20, 0, 0, 605, 607, 3, 112, 56, 0, 606, 604, 1, 0, 0, 0, 606, 607, 1, 0, 0, 0, 607, 608, 1, 0, 0, 0, 608, 609, 5, 99, 0, 0, 609, 611, 1, 0, 0, 0, 610, 568, 1, 0, 0, 0, 610, 577, 1, 0, 0, 0, 610, 587, 1, 0, 0, 0, 610, 598, 1, 0, 0, 0, 611, 69, 1, 0, 0, 0, 612, 613, 5, 104, 0, 0, 613, 618, 3, 72, 36, 0, 614, 615, 5, 100, 0, 0, 615, 617, 3, 72, 36, 0, 616, 614, 1, 0, 0, 0, 617, 620, 1, 0, 0, 0, 618, 616, 1, 0, 0, 0, 618, 619, 1, 0, 0, 0, 619, 621, 1, 0, 0, 0, 620, 618, 1, 0, 0, 0, 621, 622, 5, 105, 0, 0, 622, 71, 1, 0, 0, 0, 623, 624, 7, 1, 0, 0, 624, 73, 1, 0, 0, 0, 625, 626, 5, 27, 0, 0, 626, 627, 3, 76, 38, 0, 627, 75, 1, 0, 0, 0, 628, 631, 3, 78, 39, 0, 629, 631, 3, 82, 41, 0, 630, 628, 1, 0, 0, 0, 630, 629, 1, 0, 0, 0, 631, 77, 1, 0, 0, 0, 632, 633, 5, 28, 0, 0, 633, 634, 3, 128, 64, 0, 634, 635, 5, 49, 0, 0, 635, 636, 3, 130, 65, 0, 636, 637, 5, 37, 0, 0, 637, 638, 3, 128, 64, 0, 638, 639, 5, 98, 0, 0, 639, 640, 3, 112, 56, 0, 640, 641, 5, 38, 0, 0, 641, 644, 3, 80, 40, 0, 642, 643, 5, 39, 0, 0, 643, 645, 3, 120, 60, 0, 644, 642, 1, 0, 0, 0, 644, 645, 1, 0, 0, 0, 645, 646, 1, 0, 0, 0, 646, 647, 5, 99, 0, 0, 647, 79, 1, 0, 0, 0, 648, 655, 5, 71, 0, 0, 649, 650, 5, 72, 0, 0, 650, 651, 5, 106, 0, 0, 651, 652, 3, 112, 56, 0, 652, 653, 5, 107, 0, 0, 653, 655, 1, 0, 0, 0, 654, 648, 1, 0, 0, 0, 654, 649, 1, 0, 0, 0, 655, 81, 1, 0, 0, 0, 656, 657, 5, 29, 0, 0, 657, 658, 3, 128, 64, 0, 658, 659, 5, 49, 0, 0, 659, 660, 3, 130, 65, 0, 660, 661, 5, 102, 0, 0, 661, 662, 3, 84, 42, 0, 662, 663, 3, 84, 42, 0, 663, 664, 5, 103, 0, 0, 664, 83, 1, 0, 0, 0, 665, 666, 3, 46, 23, 0, 666, 667, 5, 30, 0, 0, 667, 668, 3, 128, 64, 0, 668, 669, 5, 49, 0, 0, 669, 670, 3, 130, 65, 0, 670, 672, 3, 118, 59, 0, 671, 673, 5, 77, 0, 0, 672, 671, 1, 0, 0, 0, 672, 673, 1, 0, 0, 0, 673, 676, 1, 0, 0, 0, 674, 675, 5, 45, 0, 0, 675, 677, 3, 130, 65, 0, 676, 674, 1, 0, 0, 0, 676, 677, 1, 0, 0, 0, 677, 679, 1, 0, 0, 0, 678, 680, 5, 46, 0, 0, 679, 678, 1, 0, 0, 0, 679, 680, 1, 0, 0, 0, 680, 683, 1, 0, 0, 0, 681, 682, 5, 47, 0, 0, 682, 684, 3, 130, 65, 0, 683, 681, 1, 0, 0, 0, 683, 684, 1, 0, 0, 0, 684, 686, 1, 0, 0, 0, 685, 687, 5, 48, 0, 0, 686, 685, 1, 0, 0, 0, 686, 687, 1, 0, 0, 0, 687, 688, 1, 0, 0, 0, 688, 689, 5, 99, 0, 0, 689, 85, 1, 0, 0, 0, 690, 691, 5, 21, 0, 0, 691, 692, 3, 128, 64, 0, 692, 693, 5, 22, 0, 0, 693, 695, 3, 128, 64, 0, 694, 696, 3, 28, 14, 0, 695, 694, 1, 0, 0, 0, 695, 696, 1, 0, 0, 0, 696, 699, 1, 0, 0, 0, 697, 698, 5, 49, 0, 0, 698, 700, 3, 130, 65, 0, 699, 697, 1, 0, 0, 0, 699, 700, 1, 0, 0, 0, 700, 703, 1, 0, 0, 0, 701, 702, 5, 44, 0, 0, 702, 704, 5, 112, 0, 0, 703, 701, 1, 0, 0, 0, 703, 704, 1, 0, 0, 0, 704, 705, 1, 0, 0, 0, 705, 709, 5, 102, 0, 0, 706, 708, 3, 88, 44, 0, 707, 706, 1, 0, 0, 0, 708, 711, 1, 0, 0, 0, 709, 707, 1, 0, 0, 0, 709, 710, 1, 0, 0, 0, 710, 712, 1, 0, 0, 0, 711, 709, 1, 0, 0, 0, 712, 713, 5, 103, 0, 0, 713, 87, 1, 0, 0, 0, 714, 715, 5, 26, 0, 0, 715, 720, 3, 76, 38, 0, 716, 720, 3, 94, 47, 0, 717, 720, 3, 90, 45, 0, 718, 720, 3, 92, 46, 0, 719, 714, 1, 0, 0, 0, 719, 716, 1, 0, 0, 0, 719, 717, 1, 0, 0, 0, 719, 718, 1, 0, 0, 0, 720, 89, 1, 0, 0, 0, 721, 722, 5, 23, 0, 0, 722, 723, 3, 128, 64, 0, 723, 724, 5, 25, 0, 0, 724, 725, 5, 28, 0, 0, 725, 726, 3, 128, 64, 0, 726, 727, 5, 99, 0, 0, 727, 91, 1, 0, 0, 0, 728, 729, 5, 34, 0, 0, 729, 730, 3, 128, 64, 0, 730, 731, 5, 35, 0, 0, 731, 732, 5, 36, 0, 0, 732, 733, 5, 32, 0, 0, 733, 734, 5, 18, 0, 0, 734, 735, 3, 128, 64, 0, 735, 736, 5, 33, 0, 0, 736, 737, 5, 29, 0, 0, 737, 738, 3, 128, 64, 0, 738, 739, 5, 101, 0, 0, 739, 740, 3, 128, 64, 0, 740, 741, 5, 99, 0, 0, 741, 93, 1, 0, 0, 0, 742, 743, 5, 23, 0, 0, 743, 744, 3, 96, 48, 0, 744, 745, 5, 25, 0, 0, 745, 746, 3, 100, 50, 0, 746, 747, 5, 99, 0, 0, 747, 95, 1, 0, 0, 0, 748, 749, 3, 128, 64, 0, 749, 750, 5, 101, 0, 0, 750, 751, 3, 98, 49, 0, 751, 97, 1, 0, 0, 0, 752, 764, 3, 128, 64, 0, 753, 764, 5, 66, 0, 0, 754, 764, 5, 56, 0, 0, 755, 764, 5, 57, 0, 0, 756, 764, 5, 63, 0, 0, 757, 764, 5, 64, 0, 0, 758, 764, 5, 65, 0, 0, 759, 764, 5, 67, 0, 0, 760, 764, 5, 68, 0, 0, 761, 764, 5, 69, 0, 0, 762, 764, 5, 70, 0, 0, 763, 752, 1, 0, 0, 0, 763, 753, 1, 0, 0, 0, 763, 754, 1, 0, 0, 0, 763, 755, 1, 0, 0, 0, 763, 756, 1, 0, 0, 0, 763, 757, 1, 0, 0, 0, 763, 758, 1, 0, 0, 0, 763, 759, 1, 0, 0, 0, 763, 760, 1, 0, 0, 0, 763, 761, 1, 0, 0, 0, 763, 762, 1, 0, 0, 0, 764, 99, 1, 0, 0, 0, 765, 766, 5, 28, 0, 0, 766, 767, 3, 128, 64, 0, 767, 768, 5, 101, 0, 0, 768, 769, 3, 102, 51, 0, 769, 788, 1, 0, 0, 0, 770, 771, 5, 29, 0, 0, 771, 772, 3, 128, 64, 0, 772, 773, 5, 101, 0, 0, 773, 774, 3, 128, 64, 0, 774, 775, 5, 101, 0, 0, 775, 776, 3, 104, 52, 0, 776, 788, 1, 0, 0, 0, 777, 778, 5, 13, 0, 0, 778, 779, 3, 128, 64, 0, 779, 780, 5, 101, 0, 0, 780, 782, 3, 128, 64, 0, 781, 783, 3, 28, 14, 0, 782, 781, 1, 0, 0, 0, 782, 783, 1, 0, 0, 0, 783, 785, 1, 0, 0, 0, 784, 786, 3, 106, 53, 0, 785, 784, 1, 0, 0, 0, 785, 786, 1, 0, 0, 0, 786, 788, 1, 0, 0, 0, 787, 765, 1, 0, 0, 0, 787, 770, 1, 0, 0, 0, 787, 777, 1, 0, 0, 0, 788, 101, 1, 0, 0, 0, 789, 790, 7, 2, 0, 0, 790, 103, 1, 0, 0, 0, 791, 792, 7, 3, 0, 0, 792, 105, 1, 0, 0, 0, 793, 794, 5, 31, 0, 0, 794, 798, 5, 102, 0, 0, 795, 797, 3, 108, 54, 0, 796, 795, 1, 0, 0, 0, 797, 800, 1, 0, 0, 0, 798, 796, 1, 0, 0, 0, 798, 799, 1, 0, 0, 0, 799, 801, 1, 0, 0, 0, 800, 798, 1, 0, 0, 0, 801, 802, 5, 103, 0, 0, 802, 107, 1, 0, 0, 0, 803, 804, 3, 128, 64, 0, 804, 805, 5, 25, 0, 0, 805, 806, 5, 28, 0, 0, 806, 813, 3, 128, 64, 0, 807, 808, 5, 33, 0, 0, 808, 809, 5, 29, 0, 0, 809, 810, 3, 128, 64, 0, 810, 811, 5, 101, 0, 0, 811, 812, 3, 128, 64, 0, 812, 814, 1, 0, 0, 0, 813, 807, 1, 0, 0, 0, 813, 814, 1, 0, 0, 0, 814, 815, 1, 0, 0, 0, 815, 816, 5, 99, 0, 0, 816, 857, 1, 0, 0, 0, 817, 818, 3, 128, 64, 0, 818, 819, 5, 25, 0, 0, 819, 820, 5, 29, 0, 0, 820, 821, 3, 128, 64, 0, 821, 822, 5, 101, 0, 0, 822, 829, 3, 128, 64, 0, 823, 824, 5, 33, 0, 0, 824, 825, 5, 29, 0, 0, 825, 826, 3, 128, 64, 0, 826, 827, 5, 101, 0, 0, 827, 828, 3, 128, 64, 0, 828, 830, 1, 0, 0, 0, 829, 823, 1, 0, 0, 0, 829, 830, 1, 0, 0, 0, 830, 831, 1, 0, 0, 0, 831, 832, 5, 99, 0, 0, 832, 857, 1, 0, 0, 0, 833, 834, 3, 128, 64, 0, 834, 835, 5, 25, 0, 0, 835, 836, 5, 11, 0, 0, 836, 838, 3, 128, 64, 0, 837, 839, 3, 28, 14, 0, 838, 837, 1, 0, 0, 0, 838, 839, 1, 0, 0, 0, 839, 846, 1, 0, 0, 0, 840, 841, 5, 33, 0, 0, 841, 842, 5, 29, 0, 0, 842, 843, 3, 128, 64, 0, 843, 844, 5, 101, 0, 0, 844, 845, 3, 128, 64, 0, 845, 847, 1, 0, 0, 0, 846, 840, 1, 0, 0, 0, 846, 847, 1, 0, 0, 0, 847, 848, 1, 0, 0, 0, 848, 849, 5, 99, 0, 0, 849, 857, 1, 0, 0, 0, 850, 851, 3, 128, 64, 0, 851, 852, 5, 25, 0, 0, 852, 853, 5, 18, 0, 0, 853, 854, 3, 128, 64, 0, 854, 855, 5, 99, 0, 0, 855, 857, 1, 0, 0, 0, 856, 803, 1, 0, 0, 0, 856, 817, 1, 0, 0, 0, 856, 833, 1, 0, 0, 0, 856, 850, 1, 0, 0, 0, 857, 109, 1, 0, 0, 0, 858, 859, 5, 18, 0, 0, 859, 860, 3, 128, 64, 0, 860, 861, 5, 25, 0, 0, 861, 862, 3, 128, 64, 0, 862, 863, 5, 101, 0, 0, 863, 865, 3, 128, 64, 0, 864, 866, 3, 106, 53, 0, 865, 864, 1, 0, 0, 0, 865, 866, 1, 0, 0, 0, 866, 867, 1, 0, 0, 0, 867, 868, 5, 99, 0, 0, 868, 111, 1, 0, 0, 0, 869, 916, 3, 116, 58, 0, 870, 916, 5, 78, 0, 0, 871, 916, 5, 79, 0, 0, 872, 873, 5, 80, 0, 0, 873, 916, 3, 130, 65, 0, 874, 875, 5, 81, 0, 0, 875, 876, 5, 108, 0, 0, 876, 877, 3, 128, 64, 0, 877, 878, 5, 109, 0, 0, 878, 916, 1, 0, 0, 0, 879, 880, 5, 82, 0, 0, 880, 881, 5, 108, 0, 0, 881, 883, 3, 128, 64, 0, 882, 884, 3, 28, 14, 0, 883, 882, 1, 0, 0, 0, 883, 884, 1, 0, 0, 0, 884, 885, 1, 0, 0, 0, 885, 886, 5, 109, 0, 0, 886, 916, 1, 0, 0, 0, 887, 888, 5, 6, 0, 0, 888, 889, 5, 108, 0, 0, 889, 890, 3, 128, 64, 0, 890, 891, 5, 109, 0, 0, 891, 916, 1, 0, 0, 0, 892, 893, 5, 83, 0, 0, 893, 894, 5, 108, 0, 0, 894, 895, 3, 112, 56, 0, 895, 896, 5, 109, 0, 0, 896, 916, 1, 0, 0, 0, 897, 898, 5, 84, 0, 0, 898, 899, 5, 108, 0, 0, 899, 900, 3, 112, 56, 0, 900, 901, 5, 109, 0, 0, 901, 916, 1, 0, 0, 0, 902, 903, 5, 85, 0, 0, 903, 907, 5, 102, 0, 0, 904, 906, 3, 114, 57, 0, 905, 904, 1, 0, 0, 0, 906, 909, 1, 0, 0, 0, 907, 905, 1, 0, 0, 0, 907, 908, 1, 0, 0, 0, 908, 910, 1, 0, 0, 0, 909, 907, 1, 0, 0, 0, 910, 916, 5, 103, 0, 0, 911, 913, 3, 128, 64, 0, 912, 914, 3, 28, 14, 0, 913, 912, 1, 0, 0, 0, 913, 914, 1, 0, 0, 0, 914, 916, 1, 0, 0, 0, 915, 869, 1, 0, 0, 0, 915, 870, 1, 0, 0, 0, 915, 871, 1, 0, 0, 0, 915, 872, 1, 0, 0, 0, 915, 874, 1, 0, 0, 0, 915, 879, 1, 0, 0, 0, 915, 887, 1, 0, 0, 0, 915, 892, 1, 0, 0, 0, 915, 897, 1, 0, 0, 0, 915, 902, 1, 0, 0, 0, 915, 911, 1, 0, 0, 0, 916, 113, 1, 0, 0, 0, 917, 918, 3, 128, 64, 0, 918, 919, 5, 98, 0, 0, 919, 920, 3, 112, 56, 0, 920, 921, 5, 99, 0, 0, 921, 115, 1, 0, 0, 0, 922, 923, 7, 4, 0, 0, 923, 117, 1, 0, 0, 0, 924, 925, 7, 5, 0, 0, 925, 119, 1, 0, 0, 0, 926, 935, 3, 130, 65, 0, 927, 935, 5, 112, 0, 0, 928, 935, 5, 113, 0, 0, 929, 935, 5, 94, 0, 0, 930, 935, 5, 95, 0, 0, 931, 935, 5, 96, 0, 0, 932, 935, 3, 122, 61, 0, 933, 935, 3, 126, 63, 0, 934, 926, 1, 0, 0, 0, 934, 927, 1, 0, 0, 0, 934, 928, 1, 0, 0, 0, 934, 929, 1, 0, 0, 0, 934, 930, 1, 0, 0, 0, 934, 931, 1, 0, 0, 0, 934, 932, 1, 0, 0, 0, 934, 933, 1, 0, 0, 0, 935, 121, 1, 0, 0, 0, 936, 945, 5, 102, 0, 0, 937, 942, 3, 124, 62, 0, 938, 939, 5, 100, 0, 0, 939, 941, 3, 124, 62, 0, 940, 938, 1, 0, 0, 0, 941, 944, 1, 0, 0, 0, 942, 940, 1, 0, 0, 0, 942, 943, 1, 0, 0, 0, 943, 946, 1, 0, 0, 0, 944, 942, 1, 0, 0, 0, 945, 937, 1, 0, 0, 0, 945, 946, 1, 0, 0, 0, 946, 947, 1, 0, 0, 0, 947, 948, 5, 103, 0, 0, 948, 123, 1, 0, 0, 0, 949, 950, 3, 130, 65, 0, 950, 951, 5, 98, 0, 0, 951, 952, 3, 120, 60, 0, 952, 125, 1, 0, 0, 0, 953, 962, 5, 104, 0, 0, 954, 959, 3, 120, 60, 0, 955, 956, 5, 100, 0, 0, 956, 958, 3, 120, 60, 0, 957, 955, 1, 0, 0, 0, 958, 961, 1, 0, 0, 0, 959, 957, 1, 0, 0, 0, 959, 960, 1, 0, 0, 0, 960, 963, 1, 0, 0, 0, 961, 959, 1, 0, 0, 0, 962, 954, 1, 0, 0, 0, 962, 963, 1, 0, 0, 0, 963, 964, 1, 0, 0, 0, 964, 965, 5, 105, 0, 0, 965, 127, 1, 0, 0, 0, 966, 967, 7, 6, 0, 0, 967, 129, 1, 0, 0, 0, 968, 969, 5, 115, 0, 0, 969, 131, 1, 0, 0, 0, 82, 144, 151, 172, 184, 196, 216, 224, 231, 237, 249, 252, 258, 269, 278, 288, 291, 293, 297, 305, 317, 322, 331, 334, 361, 385, 395, 401, 430, 437, 441, 446, 457, 463, 471, 476, 487, 492, 499, 508, 521, 542, 545, 548, 555, 563, 594, 606, 610, 618, 630, 644, 654, 672, 676, 679, 683, 686, 695, 699, 703, 709, 719, 763, 782, 785, 787, 798, 813, 829, 838, 846, 856, 865, 883, 907, 913, 915, 934, 942, 945, 959, 962] \ No newline at end of file diff --git a/src/capability-language/generated/QuixosCapability.tokens b/src/capability-language/generated/QuixosCapability.tokens index 75f42fb..ed12769 100644 --- a/src/capability-language/generated/QuixosCapability.tokens +++ b/src/capability-language/generated/QuixosCapability.tokens @@ -21,100 +21,101 @@ INPUT=20 CONFORM=21 AS=22 BIND=23 -TO=24 -PRIVATE=25 -SHARED=26 -STATE=27 -EDGE=28 -PROJECTION=29 -WITH=30 -USING=31 -VIA=32 -MATERIALIZE=33 -IF=34 -ABSENT=35 -ON=36 -POLICY=37 -DEFAULT=38 -SOURCE=39 -REPOSITORY=40 -COMMIT=41 -REVISION=42 -SEMANTIC_MAJOR=43 -ON_DELETE=44 -RETAIN_OTHER=45 -KEYED=46 -PUBLIC_TRAVERSAL=47 -ID=48 -DOC=49 -MODE=50 -EMITS=51 -RECEIVER=52 -REQUIRES=53 -ANY=54 -GET=55 -SET=56 -WATCH=57 -START=58 -STOP=59 -READ=60 -WRITE=61 -RESOLVE=62 -CONNECT=63 -DISCONNECT=64 -CALL=65 -WATCH_START=66 -WATCH_STOP=67 -SUBSCRIBE=68 -UNSUBSCRIBE=69 -OPTIMISTIC_REGISTER=70 -CRDT=71 -OPTIONAL_ONE=72 -EXACTLY_ONE=73 -MANY_UNIQUE=74 -MANY=75 -ORDERED=76 -UNIT=77 -WATCH_HANDLE=78 -MESSAGE=79 -ATOM_REF=80 -INTERFACE_REF=81 -OPTIONAL=82 -LIST=83 -RECORD=84 -BOOL=85 -BYTES=86 -DOUBLE=87 -INT32=88 -INT64=89 -STRING=90 -UINT32=91 -UINT64=92 -TRUE=93 -FALSE=94 -NULL=95 -ARROW=96 -COLON=97 -SEMI=98 -COMMA=99 -DOT=100 -LBRACE=101 -RBRACE=102 -LBRACK=103 -RBRACK=104 -LPAREN=105 -RPAREN=106 -LT=107 -GT=108 -AMP=109 -EQUAL=110 -INTEGER=111 -JSON_NUMBER=112 -IDENTIFIER=113 -STRING_LITERAL=114 -LINE_COMMENT=115 -BLOCK_COMMENT=116 -WS=117 +STATIC=24 +TO=25 +PRIVATE=26 +SHARED=27 +STATE=28 +EDGE=29 +PROJECTION=30 +WITH=31 +USING=32 +VIA=33 +MATERIALIZE=34 +IF=35 +ABSENT=36 +ON=37 +POLICY=38 +DEFAULT=39 +SOURCE=40 +REPOSITORY=41 +COMMIT=42 +REVISION=43 +SEMANTIC_MAJOR=44 +ON_DELETE=45 +RETAIN_OTHER=46 +KEYED=47 +PUBLIC_TRAVERSAL=48 +ID=49 +DOC=50 +MODE=51 +EMITS=52 +RECEIVER=53 +REQUIRES=54 +ANY=55 +GET=56 +SET=57 +WATCH=58 +START=59 +STOP=60 +READ=61 +WRITE=62 +RESOLVE=63 +CONNECT=64 +DISCONNECT=65 +CALL=66 +WATCH_START=67 +WATCH_STOP=68 +SUBSCRIBE=69 +UNSUBSCRIBE=70 +OPTIMISTIC_REGISTER=71 +CRDT=72 +OPTIONAL_ONE=73 +EXACTLY_ONE=74 +MANY_UNIQUE=75 +MANY=76 +ORDERED=77 +UNIT=78 +WATCH_HANDLE=79 +MESSAGE=80 +ATOM_REF=81 +INTERFACE_REF=82 +OPTIONAL=83 +LIST=84 +RECORD=85 +BOOL=86 +BYTES=87 +DOUBLE=88 +INT32=89 +INT64=90 +STRING=91 +UINT32=92 +UINT64=93 +TRUE=94 +FALSE=95 +NULL=96 +ARROW=97 +COLON=98 +SEMI=99 +COMMA=100 +DOT=101 +LBRACE=102 +RBRACE=103 +LBRACK=104 +RBRACK=105 +LPAREN=106 +RPAREN=107 +LT=108 +GT=109 +AMP=110 +EQUAL=111 +INTEGER=112 +JSON_NUMBER=113 +IDENTIFIER=114 +STRING_LITERAL=115 +LINE_COMMENT=116 +BLOCK_COMMENT=117 +WS=118 'workspace'=1 'type'=2 'object'=3 @@ -138,90 +139,91 @@ WS=117 'conform'=21 'as'=22 'bind'=23 -'to'=24 -'private'=25 -'shared'=26 -'state'=27 -'edge'=28 -'projection'=29 -'with'=30 -'using'=31 -'via'=32 -'materialize'=33 -'if'=34 -'absent'=35 -'on'=36 -'policy'=37 -'default'=38 -'source'=39 -'repository'=40 -'commit'=41 -'revision'=42 -'semantic-major'=43 -'on-delete'=44 -'retain-other'=45 -'keyed'=46 -'public-traversal'=47 -'id'=48 -'doc'=49 -'mode'=50 -'emits'=51 -'receiver'=52 -'requires'=53 -'any'=54 -'get'=55 -'set'=56 -'watch'=57 -'start'=58 -'stop'=59 -'read'=60 -'write'=61 -'resolve'=62 -'connect'=63 -'disconnect'=64 -'call'=65 -'watch-start'=66 -'watch-stop'=67 -'subscribe'=68 -'unsubscribe'=69 -'optimistic-register'=70 -'crdt'=71 -'optional-one'=72 -'exactly-one'=73 -'many-unique'=74 -'many'=75 -'ordered'=76 -'unit'=77 -'watch-handle'=78 -'message'=79 -'atom-ref'=80 -'interface-ref'=81 -'optional'=82 -'list'=83 -'record'=84 -'bool'=85 -'bytes'=86 -'double'=87 -'int32'=88 -'int64'=89 -'string'=90 -'uint32'=91 -'uint64'=92 -'true'=93 -'false'=94 -'null'=95 -'->'=96 -':'=97 -';'=98 -','=99 -'.'=100 -'{'=101 -'}'=102 -'['=103 -']'=104 -'('=105 -')'=106 -'<'=107 -'>'=108 -'&'=109 -'='=110 +'static'=24 +'to'=25 +'private'=26 +'shared'=27 +'state'=28 +'edge'=29 +'projection'=30 +'with'=31 +'using'=32 +'via'=33 +'materialize'=34 +'if'=35 +'absent'=36 +'on'=37 +'policy'=38 +'default'=39 +'source'=40 +'repository'=41 +'commit'=42 +'revision'=43 +'semantic-major'=44 +'on-delete'=45 +'retain-other'=46 +'keyed'=47 +'public-traversal'=48 +'id'=49 +'doc'=50 +'mode'=51 +'emits'=52 +'receiver'=53 +'requires'=54 +'any'=55 +'get'=56 +'set'=57 +'watch'=58 +'start'=59 +'stop'=60 +'read'=61 +'write'=62 +'resolve'=63 +'connect'=64 +'disconnect'=65 +'call'=66 +'watch-start'=67 +'watch-stop'=68 +'subscribe'=69 +'unsubscribe'=70 +'optimistic-register'=71 +'crdt'=72 +'optional-one'=73 +'exactly-one'=74 +'many-unique'=75 +'many'=76 +'ordered'=77 +'unit'=78 +'watch-handle'=79 +'message'=80 +'atom-ref'=81 +'interface-ref'=82 +'optional'=83 +'list'=84 +'record'=85 +'bool'=86 +'bytes'=87 +'double'=88 +'int32'=89 +'int64'=90 +'string'=91 +'uint32'=92 +'uint64'=93 +'true'=94 +'false'=95 +'null'=96 +'->'=97 +':'=98 +';'=99 +','=100 +'.'=101 +'{'=102 +'}'=103 +'['=104 +']'=105 +'('=106 +')'=107 +'<'=108 +'>'=109 +'&'=110 +'='=111 diff --git a/src/capability-language/generated/QuixosCapabilityLexer.interp b/src/capability-language/generated/QuixosCapabilityLexer.interp index 4e55c58..d67bca1 100644 --- a/src/capability-language/generated/QuixosCapabilityLexer.interp +++ b/src/capability-language/generated/QuixosCapabilityLexer.interp @@ -23,6 +23,7 @@ null 'conform' 'as' 'bind' +'static' 'to' 'private' 'shared' @@ -143,6 +144,7 @@ INPUT CONFORM AS BIND +STATIC TO PRIVATE SHARED @@ -262,6 +264,7 @@ INPUT CONFORM AS BIND +STATIC TO PRIVATE SHARED @@ -367,4 +370,4 @@ mode names: DEFAULT_MODE atn: -[4, 0, 117, 1110, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 2, 88, 7, 88, 2, 89, 7, 89, 2, 90, 7, 90, 2, 91, 7, 91, 2, 92, 7, 92, 2, 93, 7, 93, 2, 94, 7, 94, 2, 95, 7, 95, 2, 96, 7, 96, 2, 97, 7, 97, 2, 98, 7, 98, 2, 99, 7, 99, 2, 100, 7, 100, 2, 101, 7, 101, 2, 102, 7, 102, 2, 103, 7, 103, 2, 104, 7, 104, 2, 105, 7, 105, 2, 106, 7, 106, 2, 107, 7, 107, 2, 108, 7, 108, 2, 109, 7, 109, 2, 110, 7, 110, 2, 111, 7, 111, 2, 112, 7, 112, 2, 113, 7, 113, 2, 114, 7, 114, 2, 115, 7, 115, 2, 116, 7, 116, 2, 117, 7, 117, 2, 118, 7, 118, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 83, 1, 83, 1, 83, 1, 83, 1, 83, 1, 83, 1, 83, 1, 84, 1, 84, 1, 84, 1, 84, 1, 84, 1, 85, 1, 85, 1, 85, 1, 85, 1, 85, 1, 85, 1, 86, 1, 86, 1, 86, 1, 86, 1, 86, 1, 86, 1, 86, 1, 87, 1, 87, 1, 87, 1, 87, 1, 87, 1, 87, 1, 88, 1, 88, 1, 88, 1, 88, 1, 88, 1, 88, 1, 89, 1, 89, 1, 89, 1, 89, 1, 89, 1, 89, 1, 89, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 91, 1, 91, 1, 91, 1, 91, 1, 91, 1, 91, 1, 91, 1, 92, 1, 92, 1, 92, 1, 92, 1, 92, 1, 93, 1, 93, 1, 93, 1, 93, 1, 93, 1, 93, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 95, 1, 95, 1, 95, 1, 96, 1, 96, 1, 97, 1, 97, 1, 98, 1, 98, 1, 99, 1, 99, 1, 100, 1, 100, 1, 101, 1, 101, 1, 102, 1, 102, 1, 103, 1, 103, 1, 104, 1, 104, 1, 105, 1, 105, 1, 106, 1, 106, 1, 107, 1, 107, 1, 108, 1, 108, 1, 109, 1, 109, 1, 110, 3, 110, 1011, 8, 110, 1, 110, 4, 110, 1014, 8, 110, 11, 110, 12, 110, 1015, 1, 111, 3, 111, 1019, 8, 111, 1, 111, 1, 111, 1, 111, 5, 111, 1024, 8, 111, 10, 111, 12, 111, 1027, 9, 111, 3, 111, 1029, 8, 111, 1, 111, 1, 111, 4, 111, 1033, 8, 111, 11, 111, 12, 111, 1034, 3, 111, 1037, 8, 111, 1, 111, 1, 111, 3, 111, 1041, 8, 111, 1, 111, 4, 111, 1044, 8, 111, 11, 111, 12, 111, 1045, 3, 111, 1048, 8, 111, 1, 112, 1, 112, 5, 112, 1052, 8, 112, 10, 112, 12, 112, 1055, 9, 112, 1, 113, 1, 113, 1, 113, 5, 113, 1060, 8, 113, 10, 113, 12, 113, 1063, 9, 113, 1, 113, 1, 113, 1, 114, 1, 114, 1, 114, 1, 114, 1, 114, 1, 114, 1, 114, 1, 114, 3, 114, 1075, 8, 114, 1, 115, 1, 115, 1, 116, 1, 116, 1, 116, 1, 116, 5, 116, 1083, 8, 116, 10, 116, 12, 116, 1086, 9, 116, 1, 116, 1, 116, 1, 117, 1, 117, 1, 117, 1, 117, 5, 117, 1094, 8, 117, 10, 117, 12, 117, 1097, 9, 117, 1, 117, 1, 117, 1, 117, 1, 117, 1, 117, 1, 118, 4, 118, 1105, 8, 118, 11, 118, 12, 118, 1106, 1, 118, 1, 118, 1, 1095, 0, 119, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 33, 67, 34, 69, 35, 71, 36, 73, 37, 75, 38, 77, 39, 79, 40, 81, 41, 83, 42, 85, 43, 87, 44, 89, 45, 91, 46, 93, 47, 95, 48, 97, 49, 99, 50, 101, 51, 103, 52, 105, 53, 107, 54, 109, 55, 111, 56, 113, 57, 115, 58, 117, 59, 119, 60, 121, 61, 123, 62, 125, 63, 127, 64, 129, 65, 131, 66, 133, 67, 135, 68, 137, 69, 139, 70, 141, 71, 143, 72, 145, 73, 147, 74, 149, 75, 151, 76, 153, 77, 155, 78, 157, 79, 159, 80, 161, 81, 163, 82, 165, 83, 167, 84, 169, 85, 171, 86, 173, 87, 175, 88, 177, 89, 179, 90, 181, 91, 183, 92, 185, 93, 187, 94, 189, 95, 191, 96, 193, 97, 195, 98, 197, 99, 199, 100, 201, 101, 203, 102, 205, 103, 207, 104, 209, 105, 211, 106, 213, 107, 215, 108, 217, 109, 219, 110, 221, 111, 223, 112, 225, 113, 227, 114, 229, 0, 231, 0, 233, 115, 235, 116, 237, 117, 1, 0, 11, 1, 0, 48, 57, 1, 0, 49, 57, 2, 0, 69, 69, 101, 101, 2, 0, 43, 43, 45, 45, 3, 0, 65, 90, 95, 95, 97, 122, 4, 0, 48, 57, 65, 90, 95, 95, 97, 122, 4, 0, 10, 10, 13, 13, 34, 34, 92, 92, 8, 0, 34, 34, 47, 47, 92, 92, 98, 98, 102, 102, 110, 110, 114, 114, 116, 116, 3, 0, 48, 57, 65, 70, 97, 102, 2, 0, 10, 10, 13, 13, 3, 0, 9, 10, 13, 13, 32, 32, 1124, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0, 65, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 73, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, 81, 1, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0, 89, 1, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 93, 1, 0, 0, 0, 0, 95, 1, 0, 0, 0, 0, 97, 1, 0, 0, 0, 0, 99, 1, 0, 0, 0, 0, 101, 1, 0, 0, 0, 0, 103, 1, 0, 0, 0, 0, 105, 1, 0, 0, 0, 0, 107, 1, 0, 0, 0, 0, 109, 1, 0, 0, 0, 0, 111, 1, 0, 0, 0, 0, 113, 1, 0, 0, 0, 0, 115, 1, 0, 0, 0, 0, 117, 1, 0, 0, 0, 0, 119, 1, 0, 0, 0, 0, 121, 1, 0, 0, 0, 0, 123, 1, 0, 0, 0, 0, 125, 1, 0, 0, 0, 0, 127, 1, 0, 0, 0, 0, 129, 1, 0, 0, 0, 0, 131, 1, 0, 0, 0, 0, 133, 1, 0, 0, 0, 0, 135, 1, 0, 0, 0, 0, 137, 1, 0, 0, 0, 0, 139, 1, 0, 0, 0, 0, 141, 1, 0, 0, 0, 0, 143, 1, 0, 0, 0, 0, 145, 1, 0, 0, 0, 0, 147, 1, 0, 0, 0, 0, 149, 1, 0, 0, 0, 0, 151, 1, 0, 0, 0, 0, 153, 1, 0, 0, 0, 0, 155, 1, 0, 0, 0, 0, 157, 1, 0, 0, 0, 0, 159, 1, 0, 0, 0, 0, 161, 1, 0, 0, 0, 0, 163, 1, 0, 0, 0, 0, 165, 1, 0, 0, 0, 0, 167, 1, 0, 0, 0, 0, 169, 1, 0, 0, 0, 0, 171, 1, 0, 0, 0, 0, 173, 1, 0, 0, 0, 0, 175, 1, 0, 0, 0, 0, 177, 1, 0, 0, 0, 0, 179, 1, 0, 0, 0, 0, 181, 1, 0, 0, 0, 0, 183, 1, 0, 0, 0, 0, 185, 1, 0, 0, 0, 0, 187, 1, 0, 0, 0, 0, 189, 1, 0, 0, 0, 0, 191, 1, 0, 0, 0, 0, 193, 1, 0, 0, 0, 0, 195, 1, 0, 0, 0, 0, 197, 1, 0, 0, 0, 0, 199, 1, 0, 0, 0, 0, 201, 1, 0, 0, 0, 0, 203, 1, 0, 0, 0, 0, 205, 1, 0, 0, 0, 0, 207, 1, 0, 0, 0, 0, 209, 1, 0, 0, 0, 0, 211, 1, 0, 0, 0, 0, 213, 1, 0, 0, 0, 0, 215, 1, 0, 0, 0, 0, 217, 1, 0, 0, 0, 0, 219, 1, 0, 0, 0, 0, 221, 1, 0, 0, 0, 0, 223, 1, 0, 0, 0, 0, 225, 1, 0, 0, 0, 0, 227, 1, 0, 0, 0, 0, 233, 1, 0, 0, 0, 0, 235, 1, 0, 0, 0, 0, 237, 1, 0, 0, 0, 1, 239, 1, 0, 0, 0, 3, 249, 1, 0, 0, 0, 5, 254, 1, 0, 0, 0, 7, 261, 1, 0, 0, 0, 9, 270, 1, 0, 0, 0, 11, 281, 1, 0, 0, 0, 13, 285, 1, 0, 0, 0, 15, 294, 1, 0, 0, 0, 17, 301, 1, 0, 0, 0, 19, 310, 1, 0, 0, 0, 21, 315, 1, 0, 0, 0, 23, 325, 1, 0, 0, 0, 25, 336, 1, 0, 0, 0, 27, 344, 1, 0, 0, 0, 29, 350, 1, 0, 0, 0, 31, 359, 1, 0, 0, 0, 33, 369, 1, 0, 0, 0, 35, 378, 1, 0, 0, 0, 37, 390, 1, 0, 0, 0, 39, 401, 1, 0, 0, 0, 41, 407, 1, 0, 0, 0, 43, 415, 1, 0, 0, 0, 45, 418, 1, 0, 0, 0, 47, 423, 1, 0, 0, 0, 49, 426, 1, 0, 0, 0, 51, 434, 1, 0, 0, 0, 53, 441, 1, 0, 0, 0, 55, 447, 1, 0, 0, 0, 57, 452, 1, 0, 0, 0, 59, 463, 1, 0, 0, 0, 61, 468, 1, 0, 0, 0, 63, 474, 1, 0, 0, 0, 65, 478, 1, 0, 0, 0, 67, 490, 1, 0, 0, 0, 69, 493, 1, 0, 0, 0, 71, 500, 1, 0, 0, 0, 73, 503, 1, 0, 0, 0, 75, 510, 1, 0, 0, 0, 77, 518, 1, 0, 0, 0, 79, 525, 1, 0, 0, 0, 81, 536, 1, 0, 0, 0, 83, 543, 1, 0, 0, 0, 85, 552, 1, 0, 0, 0, 87, 567, 1, 0, 0, 0, 89, 577, 1, 0, 0, 0, 91, 590, 1, 0, 0, 0, 93, 596, 1, 0, 0, 0, 95, 613, 1, 0, 0, 0, 97, 616, 1, 0, 0, 0, 99, 620, 1, 0, 0, 0, 101, 625, 1, 0, 0, 0, 103, 631, 1, 0, 0, 0, 105, 640, 1, 0, 0, 0, 107, 649, 1, 0, 0, 0, 109, 653, 1, 0, 0, 0, 111, 657, 1, 0, 0, 0, 113, 661, 1, 0, 0, 0, 115, 667, 1, 0, 0, 0, 117, 673, 1, 0, 0, 0, 119, 678, 1, 0, 0, 0, 121, 683, 1, 0, 0, 0, 123, 689, 1, 0, 0, 0, 125, 697, 1, 0, 0, 0, 127, 705, 1, 0, 0, 0, 129, 716, 1, 0, 0, 0, 131, 721, 1, 0, 0, 0, 133, 733, 1, 0, 0, 0, 135, 744, 1, 0, 0, 0, 137, 754, 1, 0, 0, 0, 139, 766, 1, 0, 0, 0, 141, 786, 1, 0, 0, 0, 143, 791, 1, 0, 0, 0, 145, 804, 1, 0, 0, 0, 147, 816, 1, 0, 0, 0, 149, 828, 1, 0, 0, 0, 151, 833, 1, 0, 0, 0, 153, 841, 1, 0, 0, 0, 155, 846, 1, 0, 0, 0, 157, 859, 1, 0, 0, 0, 159, 867, 1, 0, 0, 0, 161, 876, 1, 0, 0, 0, 163, 890, 1, 0, 0, 0, 165, 899, 1, 0, 0, 0, 167, 904, 1, 0, 0, 0, 169, 911, 1, 0, 0, 0, 171, 916, 1, 0, 0, 0, 173, 922, 1, 0, 0, 0, 175, 929, 1, 0, 0, 0, 177, 935, 1, 0, 0, 0, 179, 941, 1, 0, 0, 0, 181, 948, 1, 0, 0, 0, 183, 955, 1, 0, 0, 0, 185, 962, 1, 0, 0, 0, 187, 967, 1, 0, 0, 0, 189, 973, 1, 0, 0, 0, 191, 978, 1, 0, 0, 0, 193, 981, 1, 0, 0, 0, 195, 983, 1, 0, 0, 0, 197, 985, 1, 0, 0, 0, 199, 987, 1, 0, 0, 0, 201, 989, 1, 0, 0, 0, 203, 991, 1, 0, 0, 0, 205, 993, 1, 0, 0, 0, 207, 995, 1, 0, 0, 0, 209, 997, 1, 0, 0, 0, 211, 999, 1, 0, 0, 0, 213, 1001, 1, 0, 0, 0, 215, 1003, 1, 0, 0, 0, 217, 1005, 1, 0, 0, 0, 219, 1007, 1, 0, 0, 0, 221, 1010, 1, 0, 0, 0, 223, 1018, 1, 0, 0, 0, 225, 1049, 1, 0, 0, 0, 227, 1056, 1, 0, 0, 0, 229, 1066, 1, 0, 0, 0, 231, 1076, 1, 0, 0, 0, 233, 1078, 1, 0, 0, 0, 235, 1089, 1, 0, 0, 0, 237, 1104, 1, 0, 0, 0, 239, 240, 5, 119, 0, 0, 240, 241, 5, 111, 0, 0, 241, 242, 5, 114, 0, 0, 242, 243, 5, 107, 0, 0, 243, 244, 5, 115, 0, 0, 244, 245, 5, 112, 0, 0, 245, 246, 5, 97, 0, 0, 246, 247, 5, 99, 0, 0, 247, 248, 5, 101, 0, 0, 248, 2, 1, 0, 0, 0, 249, 250, 5, 116, 0, 0, 250, 251, 5, 121, 0, 0, 251, 252, 5, 112, 0, 0, 252, 253, 5, 101, 0, 0, 253, 4, 1, 0, 0, 0, 254, 255, 5, 111, 0, 0, 255, 256, 5, 98, 0, 0, 256, 257, 5, 106, 0, 0, 257, 258, 5, 101, 0, 0, 258, 259, 5, 99, 0, 0, 259, 260, 5, 116, 0, 0, 260, 6, 1, 0, 0, 0, 261, 262, 5, 115, 0, 0, 262, 263, 5, 116, 0, 0, 263, 264, 5, 111, 0, 0, 264, 265, 5, 114, 0, 0, 265, 266, 5, 97, 0, 0, 266, 267, 5, 98, 0, 0, 267, 268, 5, 108, 0, 0, 268, 269, 5, 101, 0, 0, 269, 8, 1, 0, 0, 0, 270, 271, 5, 105, 0, 0, 271, 272, 5, 109, 0, 0, 272, 273, 5, 112, 0, 0, 273, 274, 5, 108, 0, 0, 274, 275, 5, 101, 0, 0, 275, 276, 5, 109, 0, 0, 276, 277, 5, 101, 0, 0, 277, 278, 5, 110, 0, 0, 278, 279, 5, 116, 0, 0, 279, 280, 5, 115, 0, 0, 280, 10, 1, 0, 0, 0, 281, 282, 5, 114, 0, 0, 282, 283, 5, 101, 0, 0, 283, 284, 5, 102, 0, 0, 284, 12, 1, 0, 0, 0, 285, 286, 5, 102, 0, 0, 286, 287, 5, 114, 0, 0, 287, 288, 5, 97, 0, 0, 288, 289, 5, 103, 0, 0, 289, 290, 5, 109, 0, 0, 290, 291, 5, 101, 0, 0, 291, 292, 5, 110, 0, 0, 292, 293, 5, 116, 0, 0, 293, 14, 1, 0, 0, 0, 294, 295, 5, 105, 0, 0, 295, 296, 5, 109, 0, 0, 296, 297, 5, 112, 0, 0, 297, 298, 5, 111, 0, 0, 298, 299, 5, 114, 0, 0, 299, 300, 5, 116, 0, 0, 300, 16, 1, 0, 0, 0, 301, 302, 5, 101, 0, 0, 302, 303, 5, 120, 0, 0, 303, 304, 5, 116, 0, 0, 304, 305, 5, 101, 0, 0, 305, 306, 5, 114, 0, 0, 306, 307, 5, 110, 0, 0, 307, 308, 5, 97, 0, 0, 308, 309, 5, 108, 0, 0, 309, 18, 1, 0, 0, 0, 310, 311, 5, 97, 0, 0, 311, 312, 5, 116, 0, 0, 312, 313, 5, 111, 0, 0, 313, 314, 5, 109, 0, 0, 314, 20, 1, 0, 0, 0, 315, 316, 5, 105, 0, 0, 316, 317, 5, 110, 0, 0, 317, 318, 5, 116, 0, 0, 318, 319, 5, 101, 0, 0, 319, 320, 5, 114, 0, 0, 320, 321, 5, 102, 0, 0, 321, 322, 5, 97, 0, 0, 322, 323, 5, 99, 0, 0, 323, 324, 5, 101, 0, 0, 324, 22, 1, 0, 0, 0, 325, 326, 5, 105, 0, 0, 326, 327, 5, 110, 0, 0, 327, 328, 5, 116, 0, 0, 328, 329, 5, 101, 0, 0, 329, 330, 5, 114, 0, 0, 330, 331, 5, 102, 0, 0, 331, 332, 5, 97, 0, 0, 332, 333, 5, 99, 0, 0, 333, 334, 5, 101, 0, 0, 334, 335, 5, 115, 0, 0, 335, 24, 1, 0, 0, 0, 336, 337, 5, 112, 0, 0, 337, 338, 5, 97, 0, 0, 338, 339, 5, 99, 0, 0, 339, 340, 5, 107, 0, 0, 340, 341, 5, 97, 0, 0, 341, 342, 5, 103, 0, 0, 342, 343, 5, 101, 0, 0, 343, 26, 1, 0, 0, 0, 344, 345, 5, 118, 0, 0, 345, 346, 5, 97, 0, 0, 346, 347, 5, 108, 0, 0, 347, 348, 5, 117, 0, 0, 348, 349, 5, 101, 0, 0, 349, 28, 1, 0, 0, 0, 350, 351, 5, 114, 0, 0, 351, 352, 5, 101, 0, 0, 352, 353, 5, 108, 0, 0, 353, 354, 5, 97, 0, 0, 354, 355, 5, 116, 0, 0, 355, 356, 5, 105, 0, 0, 356, 357, 5, 111, 0, 0, 357, 358, 5, 110, 0, 0, 358, 30, 1, 0, 0, 0, 359, 360, 5, 111, 0, 0, 360, 361, 5, 112, 0, 0, 361, 362, 5, 101, 0, 0, 362, 363, 5, 114, 0, 0, 363, 364, 5, 97, 0, 0, 364, 365, 5, 116, 0, 0, 365, 366, 5, 105, 0, 0, 366, 367, 5, 111, 0, 0, 367, 368, 5, 110, 0, 0, 368, 32, 1, 0, 0, 0, 369, 370, 5, 102, 0, 0, 370, 371, 5, 117, 0, 0, 371, 372, 5, 110, 0, 0, 372, 373, 5, 99, 0, 0, 373, 374, 5, 116, 0, 0, 374, 375, 5, 105, 0, 0, 375, 376, 5, 111, 0, 0, 376, 377, 5, 110, 0, 0, 377, 34, 1, 0, 0, 0, 378, 379, 5, 99, 0, 0, 379, 380, 5, 111, 0, 0, 380, 381, 5, 110, 0, 0, 381, 382, 5, 115, 0, 0, 382, 383, 5, 116, 0, 0, 383, 384, 5, 114, 0, 0, 384, 385, 5, 117, 0, 0, 385, 386, 5, 99, 0, 0, 386, 387, 5, 116, 0, 0, 387, 388, 5, 111, 0, 0, 388, 389, 5, 114, 0, 0, 389, 36, 1, 0, 0, 0, 390, 391, 5, 99, 0, 0, 391, 392, 5, 111, 0, 0, 392, 393, 5, 110, 0, 0, 393, 394, 5, 115, 0, 0, 394, 395, 5, 116, 0, 0, 395, 396, 5, 114, 0, 0, 396, 397, 5, 117, 0, 0, 397, 398, 5, 99, 0, 0, 398, 399, 5, 116, 0, 0, 399, 400, 5, 115, 0, 0, 400, 38, 1, 0, 0, 0, 401, 402, 5, 105, 0, 0, 402, 403, 5, 110, 0, 0, 403, 404, 5, 112, 0, 0, 404, 405, 5, 117, 0, 0, 405, 406, 5, 116, 0, 0, 406, 40, 1, 0, 0, 0, 407, 408, 5, 99, 0, 0, 408, 409, 5, 111, 0, 0, 409, 410, 5, 110, 0, 0, 410, 411, 5, 102, 0, 0, 411, 412, 5, 111, 0, 0, 412, 413, 5, 114, 0, 0, 413, 414, 5, 109, 0, 0, 414, 42, 1, 0, 0, 0, 415, 416, 5, 97, 0, 0, 416, 417, 5, 115, 0, 0, 417, 44, 1, 0, 0, 0, 418, 419, 5, 98, 0, 0, 419, 420, 5, 105, 0, 0, 420, 421, 5, 110, 0, 0, 421, 422, 5, 100, 0, 0, 422, 46, 1, 0, 0, 0, 423, 424, 5, 116, 0, 0, 424, 425, 5, 111, 0, 0, 425, 48, 1, 0, 0, 0, 426, 427, 5, 112, 0, 0, 427, 428, 5, 114, 0, 0, 428, 429, 5, 105, 0, 0, 429, 430, 5, 118, 0, 0, 430, 431, 5, 97, 0, 0, 431, 432, 5, 116, 0, 0, 432, 433, 5, 101, 0, 0, 433, 50, 1, 0, 0, 0, 434, 435, 5, 115, 0, 0, 435, 436, 5, 104, 0, 0, 436, 437, 5, 97, 0, 0, 437, 438, 5, 114, 0, 0, 438, 439, 5, 101, 0, 0, 439, 440, 5, 100, 0, 0, 440, 52, 1, 0, 0, 0, 441, 442, 5, 115, 0, 0, 442, 443, 5, 116, 0, 0, 443, 444, 5, 97, 0, 0, 444, 445, 5, 116, 0, 0, 445, 446, 5, 101, 0, 0, 446, 54, 1, 0, 0, 0, 447, 448, 5, 101, 0, 0, 448, 449, 5, 100, 0, 0, 449, 450, 5, 103, 0, 0, 450, 451, 5, 101, 0, 0, 451, 56, 1, 0, 0, 0, 452, 453, 5, 112, 0, 0, 453, 454, 5, 114, 0, 0, 454, 455, 5, 111, 0, 0, 455, 456, 5, 106, 0, 0, 456, 457, 5, 101, 0, 0, 457, 458, 5, 99, 0, 0, 458, 459, 5, 116, 0, 0, 459, 460, 5, 105, 0, 0, 460, 461, 5, 111, 0, 0, 461, 462, 5, 110, 0, 0, 462, 58, 1, 0, 0, 0, 463, 464, 5, 119, 0, 0, 464, 465, 5, 105, 0, 0, 465, 466, 5, 116, 0, 0, 466, 467, 5, 104, 0, 0, 467, 60, 1, 0, 0, 0, 468, 469, 5, 117, 0, 0, 469, 470, 5, 115, 0, 0, 470, 471, 5, 105, 0, 0, 471, 472, 5, 110, 0, 0, 472, 473, 5, 103, 0, 0, 473, 62, 1, 0, 0, 0, 474, 475, 5, 118, 0, 0, 475, 476, 5, 105, 0, 0, 476, 477, 5, 97, 0, 0, 477, 64, 1, 0, 0, 0, 478, 479, 5, 109, 0, 0, 479, 480, 5, 97, 0, 0, 480, 481, 5, 116, 0, 0, 481, 482, 5, 101, 0, 0, 482, 483, 5, 114, 0, 0, 483, 484, 5, 105, 0, 0, 484, 485, 5, 97, 0, 0, 485, 486, 5, 108, 0, 0, 486, 487, 5, 105, 0, 0, 487, 488, 5, 122, 0, 0, 488, 489, 5, 101, 0, 0, 489, 66, 1, 0, 0, 0, 490, 491, 5, 105, 0, 0, 491, 492, 5, 102, 0, 0, 492, 68, 1, 0, 0, 0, 493, 494, 5, 97, 0, 0, 494, 495, 5, 98, 0, 0, 495, 496, 5, 115, 0, 0, 496, 497, 5, 101, 0, 0, 497, 498, 5, 110, 0, 0, 498, 499, 5, 116, 0, 0, 499, 70, 1, 0, 0, 0, 500, 501, 5, 111, 0, 0, 501, 502, 5, 110, 0, 0, 502, 72, 1, 0, 0, 0, 503, 504, 5, 112, 0, 0, 504, 505, 5, 111, 0, 0, 505, 506, 5, 108, 0, 0, 506, 507, 5, 105, 0, 0, 507, 508, 5, 99, 0, 0, 508, 509, 5, 121, 0, 0, 509, 74, 1, 0, 0, 0, 510, 511, 5, 100, 0, 0, 511, 512, 5, 101, 0, 0, 512, 513, 5, 102, 0, 0, 513, 514, 5, 97, 0, 0, 514, 515, 5, 117, 0, 0, 515, 516, 5, 108, 0, 0, 516, 517, 5, 116, 0, 0, 517, 76, 1, 0, 0, 0, 518, 519, 5, 115, 0, 0, 519, 520, 5, 111, 0, 0, 520, 521, 5, 117, 0, 0, 521, 522, 5, 114, 0, 0, 522, 523, 5, 99, 0, 0, 523, 524, 5, 101, 0, 0, 524, 78, 1, 0, 0, 0, 525, 526, 5, 114, 0, 0, 526, 527, 5, 101, 0, 0, 527, 528, 5, 112, 0, 0, 528, 529, 5, 111, 0, 0, 529, 530, 5, 115, 0, 0, 530, 531, 5, 105, 0, 0, 531, 532, 5, 116, 0, 0, 532, 533, 5, 111, 0, 0, 533, 534, 5, 114, 0, 0, 534, 535, 5, 121, 0, 0, 535, 80, 1, 0, 0, 0, 536, 537, 5, 99, 0, 0, 537, 538, 5, 111, 0, 0, 538, 539, 5, 109, 0, 0, 539, 540, 5, 109, 0, 0, 540, 541, 5, 105, 0, 0, 541, 542, 5, 116, 0, 0, 542, 82, 1, 0, 0, 0, 543, 544, 5, 114, 0, 0, 544, 545, 5, 101, 0, 0, 545, 546, 5, 118, 0, 0, 546, 547, 5, 105, 0, 0, 547, 548, 5, 115, 0, 0, 548, 549, 5, 105, 0, 0, 549, 550, 5, 111, 0, 0, 550, 551, 5, 110, 0, 0, 551, 84, 1, 0, 0, 0, 552, 553, 5, 115, 0, 0, 553, 554, 5, 101, 0, 0, 554, 555, 5, 109, 0, 0, 555, 556, 5, 97, 0, 0, 556, 557, 5, 110, 0, 0, 557, 558, 5, 116, 0, 0, 558, 559, 5, 105, 0, 0, 559, 560, 5, 99, 0, 0, 560, 561, 5, 45, 0, 0, 561, 562, 5, 109, 0, 0, 562, 563, 5, 97, 0, 0, 563, 564, 5, 106, 0, 0, 564, 565, 5, 111, 0, 0, 565, 566, 5, 114, 0, 0, 566, 86, 1, 0, 0, 0, 567, 568, 5, 111, 0, 0, 568, 569, 5, 110, 0, 0, 569, 570, 5, 45, 0, 0, 570, 571, 5, 100, 0, 0, 571, 572, 5, 101, 0, 0, 572, 573, 5, 108, 0, 0, 573, 574, 5, 101, 0, 0, 574, 575, 5, 116, 0, 0, 575, 576, 5, 101, 0, 0, 576, 88, 1, 0, 0, 0, 577, 578, 5, 114, 0, 0, 578, 579, 5, 101, 0, 0, 579, 580, 5, 116, 0, 0, 580, 581, 5, 97, 0, 0, 581, 582, 5, 105, 0, 0, 582, 583, 5, 110, 0, 0, 583, 584, 5, 45, 0, 0, 584, 585, 5, 111, 0, 0, 585, 586, 5, 116, 0, 0, 586, 587, 5, 104, 0, 0, 587, 588, 5, 101, 0, 0, 588, 589, 5, 114, 0, 0, 589, 90, 1, 0, 0, 0, 590, 591, 5, 107, 0, 0, 591, 592, 5, 101, 0, 0, 592, 593, 5, 121, 0, 0, 593, 594, 5, 101, 0, 0, 594, 595, 5, 100, 0, 0, 595, 92, 1, 0, 0, 0, 596, 597, 5, 112, 0, 0, 597, 598, 5, 117, 0, 0, 598, 599, 5, 98, 0, 0, 599, 600, 5, 108, 0, 0, 600, 601, 5, 105, 0, 0, 601, 602, 5, 99, 0, 0, 602, 603, 5, 45, 0, 0, 603, 604, 5, 116, 0, 0, 604, 605, 5, 114, 0, 0, 605, 606, 5, 97, 0, 0, 606, 607, 5, 118, 0, 0, 607, 608, 5, 101, 0, 0, 608, 609, 5, 114, 0, 0, 609, 610, 5, 115, 0, 0, 610, 611, 5, 97, 0, 0, 611, 612, 5, 108, 0, 0, 612, 94, 1, 0, 0, 0, 613, 614, 5, 105, 0, 0, 614, 615, 5, 100, 0, 0, 615, 96, 1, 0, 0, 0, 616, 617, 5, 100, 0, 0, 617, 618, 5, 111, 0, 0, 618, 619, 5, 99, 0, 0, 619, 98, 1, 0, 0, 0, 620, 621, 5, 109, 0, 0, 621, 622, 5, 111, 0, 0, 622, 623, 5, 100, 0, 0, 623, 624, 5, 101, 0, 0, 624, 100, 1, 0, 0, 0, 625, 626, 5, 101, 0, 0, 626, 627, 5, 109, 0, 0, 627, 628, 5, 105, 0, 0, 628, 629, 5, 116, 0, 0, 629, 630, 5, 115, 0, 0, 630, 102, 1, 0, 0, 0, 631, 632, 5, 114, 0, 0, 632, 633, 5, 101, 0, 0, 633, 634, 5, 99, 0, 0, 634, 635, 5, 101, 0, 0, 635, 636, 5, 105, 0, 0, 636, 637, 5, 118, 0, 0, 637, 638, 5, 101, 0, 0, 638, 639, 5, 114, 0, 0, 639, 104, 1, 0, 0, 0, 640, 641, 5, 114, 0, 0, 641, 642, 5, 101, 0, 0, 642, 643, 5, 113, 0, 0, 643, 644, 5, 117, 0, 0, 644, 645, 5, 105, 0, 0, 645, 646, 5, 114, 0, 0, 646, 647, 5, 101, 0, 0, 647, 648, 5, 115, 0, 0, 648, 106, 1, 0, 0, 0, 649, 650, 5, 97, 0, 0, 650, 651, 5, 110, 0, 0, 651, 652, 5, 121, 0, 0, 652, 108, 1, 0, 0, 0, 653, 654, 5, 103, 0, 0, 654, 655, 5, 101, 0, 0, 655, 656, 5, 116, 0, 0, 656, 110, 1, 0, 0, 0, 657, 658, 5, 115, 0, 0, 658, 659, 5, 101, 0, 0, 659, 660, 5, 116, 0, 0, 660, 112, 1, 0, 0, 0, 661, 662, 5, 119, 0, 0, 662, 663, 5, 97, 0, 0, 663, 664, 5, 116, 0, 0, 664, 665, 5, 99, 0, 0, 665, 666, 5, 104, 0, 0, 666, 114, 1, 0, 0, 0, 667, 668, 5, 115, 0, 0, 668, 669, 5, 116, 0, 0, 669, 670, 5, 97, 0, 0, 670, 671, 5, 114, 0, 0, 671, 672, 5, 116, 0, 0, 672, 116, 1, 0, 0, 0, 673, 674, 5, 115, 0, 0, 674, 675, 5, 116, 0, 0, 675, 676, 5, 111, 0, 0, 676, 677, 5, 112, 0, 0, 677, 118, 1, 0, 0, 0, 678, 679, 5, 114, 0, 0, 679, 680, 5, 101, 0, 0, 680, 681, 5, 97, 0, 0, 681, 682, 5, 100, 0, 0, 682, 120, 1, 0, 0, 0, 683, 684, 5, 119, 0, 0, 684, 685, 5, 114, 0, 0, 685, 686, 5, 105, 0, 0, 686, 687, 5, 116, 0, 0, 687, 688, 5, 101, 0, 0, 688, 122, 1, 0, 0, 0, 689, 690, 5, 114, 0, 0, 690, 691, 5, 101, 0, 0, 691, 692, 5, 115, 0, 0, 692, 693, 5, 111, 0, 0, 693, 694, 5, 108, 0, 0, 694, 695, 5, 118, 0, 0, 695, 696, 5, 101, 0, 0, 696, 124, 1, 0, 0, 0, 697, 698, 5, 99, 0, 0, 698, 699, 5, 111, 0, 0, 699, 700, 5, 110, 0, 0, 700, 701, 5, 110, 0, 0, 701, 702, 5, 101, 0, 0, 702, 703, 5, 99, 0, 0, 703, 704, 5, 116, 0, 0, 704, 126, 1, 0, 0, 0, 705, 706, 5, 100, 0, 0, 706, 707, 5, 105, 0, 0, 707, 708, 5, 115, 0, 0, 708, 709, 5, 99, 0, 0, 709, 710, 5, 111, 0, 0, 710, 711, 5, 110, 0, 0, 711, 712, 5, 110, 0, 0, 712, 713, 5, 101, 0, 0, 713, 714, 5, 99, 0, 0, 714, 715, 5, 116, 0, 0, 715, 128, 1, 0, 0, 0, 716, 717, 5, 99, 0, 0, 717, 718, 5, 97, 0, 0, 718, 719, 5, 108, 0, 0, 719, 720, 5, 108, 0, 0, 720, 130, 1, 0, 0, 0, 721, 722, 5, 119, 0, 0, 722, 723, 5, 97, 0, 0, 723, 724, 5, 116, 0, 0, 724, 725, 5, 99, 0, 0, 725, 726, 5, 104, 0, 0, 726, 727, 5, 45, 0, 0, 727, 728, 5, 115, 0, 0, 728, 729, 5, 116, 0, 0, 729, 730, 5, 97, 0, 0, 730, 731, 5, 114, 0, 0, 731, 732, 5, 116, 0, 0, 732, 132, 1, 0, 0, 0, 733, 734, 5, 119, 0, 0, 734, 735, 5, 97, 0, 0, 735, 736, 5, 116, 0, 0, 736, 737, 5, 99, 0, 0, 737, 738, 5, 104, 0, 0, 738, 739, 5, 45, 0, 0, 739, 740, 5, 115, 0, 0, 740, 741, 5, 116, 0, 0, 741, 742, 5, 111, 0, 0, 742, 743, 5, 112, 0, 0, 743, 134, 1, 0, 0, 0, 744, 745, 5, 115, 0, 0, 745, 746, 5, 117, 0, 0, 746, 747, 5, 98, 0, 0, 747, 748, 5, 115, 0, 0, 748, 749, 5, 99, 0, 0, 749, 750, 5, 114, 0, 0, 750, 751, 5, 105, 0, 0, 751, 752, 5, 98, 0, 0, 752, 753, 5, 101, 0, 0, 753, 136, 1, 0, 0, 0, 754, 755, 5, 117, 0, 0, 755, 756, 5, 110, 0, 0, 756, 757, 5, 115, 0, 0, 757, 758, 5, 117, 0, 0, 758, 759, 5, 98, 0, 0, 759, 760, 5, 115, 0, 0, 760, 761, 5, 99, 0, 0, 761, 762, 5, 114, 0, 0, 762, 763, 5, 105, 0, 0, 763, 764, 5, 98, 0, 0, 764, 765, 5, 101, 0, 0, 765, 138, 1, 0, 0, 0, 766, 767, 5, 111, 0, 0, 767, 768, 5, 112, 0, 0, 768, 769, 5, 116, 0, 0, 769, 770, 5, 105, 0, 0, 770, 771, 5, 109, 0, 0, 771, 772, 5, 105, 0, 0, 772, 773, 5, 115, 0, 0, 773, 774, 5, 116, 0, 0, 774, 775, 5, 105, 0, 0, 775, 776, 5, 99, 0, 0, 776, 777, 5, 45, 0, 0, 777, 778, 5, 114, 0, 0, 778, 779, 5, 101, 0, 0, 779, 780, 5, 103, 0, 0, 780, 781, 5, 105, 0, 0, 781, 782, 5, 115, 0, 0, 782, 783, 5, 116, 0, 0, 783, 784, 5, 101, 0, 0, 784, 785, 5, 114, 0, 0, 785, 140, 1, 0, 0, 0, 786, 787, 5, 99, 0, 0, 787, 788, 5, 114, 0, 0, 788, 789, 5, 100, 0, 0, 789, 790, 5, 116, 0, 0, 790, 142, 1, 0, 0, 0, 791, 792, 5, 111, 0, 0, 792, 793, 5, 112, 0, 0, 793, 794, 5, 116, 0, 0, 794, 795, 5, 105, 0, 0, 795, 796, 5, 111, 0, 0, 796, 797, 5, 110, 0, 0, 797, 798, 5, 97, 0, 0, 798, 799, 5, 108, 0, 0, 799, 800, 5, 45, 0, 0, 800, 801, 5, 111, 0, 0, 801, 802, 5, 110, 0, 0, 802, 803, 5, 101, 0, 0, 803, 144, 1, 0, 0, 0, 804, 805, 5, 101, 0, 0, 805, 806, 5, 120, 0, 0, 806, 807, 5, 97, 0, 0, 807, 808, 5, 99, 0, 0, 808, 809, 5, 116, 0, 0, 809, 810, 5, 108, 0, 0, 810, 811, 5, 121, 0, 0, 811, 812, 5, 45, 0, 0, 812, 813, 5, 111, 0, 0, 813, 814, 5, 110, 0, 0, 814, 815, 5, 101, 0, 0, 815, 146, 1, 0, 0, 0, 816, 817, 5, 109, 0, 0, 817, 818, 5, 97, 0, 0, 818, 819, 5, 110, 0, 0, 819, 820, 5, 121, 0, 0, 820, 821, 5, 45, 0, 0, 821, 822, 5, 117, 0, 0, 822, 823, 5, 110, 0, 0, 823, 824, 5, 105, 0, 0, 824, 825, 5, 113, 0, 0, 825, 826, 5, 117, 0, 0, 826, 827, 5, 101, 0, 0, 827, 148, 1, 0, 0, 0, 828, 829, 5, 109, 0, 0, 829, 830, 5, 97, 0, 0, 830, 831, 5, 110, 0, 0, 831, 832, 5, 121, 0, 0, 832, 150, 1, 0, 0, 0, 833, 834, 5, 111, 0, 0, 834, 835, 5, 114, 0, 0, 835, 836, 5, 100, 0, 0, 836, 837, 5, 101, 0, 0, 837, 838, 5, 114, 0, 0, 838, 839, 5, 101, 0, 0, 839, 840, 5, 100, 0, 0, 840, 152, 1, 0, 0, 0, 841, 842, 5, 117, 0, 0, 842, 843, 5, 110, 0, 0, 843, 844, 5, 105, 0, 0, 844, 845, 5, 116, 0, 0, 845, 154, 1, 0, 0, 0, 846, 847, 5, 119, 0, 0, 847, 848, 5, 97, 0, 0, 848, 849, 5, 116, 0, 0, 849, 850, 5, 99, 0, 0, 850, 851, 5, 104, 0, 0, 851, 852, 5, 45, 0, 0, 852, 853, 5, 104, 0, 0, 853, 854, 5, 97, 0, 0, 854, 855, 5, 110, 0, 0, 855, 856, 5, 100, 0, 0, 856, 857, 5, 108, 0, 0, 857, 858, 5, 101, 0, 0, 858, 156, 1, 0, 0, 0, 859, 860, 5, 109, 0, 0, 860, 861, 5, 101, 0, 0, 861, 862, 5, 115, 0, 0, 862, 863, 5, 115, 0, 0, 863, 864, 5, 97, 0, 0, 864, 865, 5, 103, 0, 0, 865, 866, 5, 101, 0, 0, 866, 158, 1, 0, 0, 0, 867, 868, 5, 97, 0, 0, 868, 869, 5, 116, 0, 0, 869, 870, 5, 111, 0, 0, 870, 871, 5, 109, 0, 0, 871, 872, 5, 45, 0, 0, 872, 873, 5, 114, 0, 0, 873, 874, 5, 101, 0, 0, 874, 875, 5, 102, 0, 0, 875, 160, 1, 0, 0, 0, 876, 877, 5, 105, 0, 0, 877, 878, 5, 110, 0, 0, 878, 879, 5, 116, 0, 0, 879, 880, 5, 101, 0, 0, 880, 881, 5, 114, 0, 0, 881, 882, 5, 102, 0, 0, 882, 883, 5, 97, 0, 0, 883, 884, 5, 99, 0, 0, 884, 885, 5, 101, 0, 0, 885, 886, 5, 45, 0, 0, 886, 887, 5, 114, 0, 0, 887, 888, 5, 101, 0, 0, 888, 889, 5, 102, 0, 0, 889, 162, 1, 0, 0, 0, 890, 891, 5, 111, 0, 0, 891, 892, 5, 112, 0, 0, 892, 893, 5, 116, 0, 0, 893, 894, 5, 105, 0, 0, 894, 895, 5, 111, 0, 0, 895, 896, 5, 110, 0, 0, 896, 897, 5, 97, 0, 0, 897, 898, 5, 108, 0, 0, 898, 164, 1, 0, 0, 0, 899, 900, 5, 108, 0, 0, 900, 901, 5, 105, 0, 0, 901, 902, 5, 115, 0, 0, 902, 903, 5, 116, 0, 0, 903, 166, 1, 0, 0, 0, 904, 905, 5, 114, 0, 0, 905, 906, 5, 101, 0, 0, 906, 907, 5, 99, 0, 0, 907, 908, 5, 111, 0, 0, 908, 909, 5, 114, 0, 0, 909, 910, 5, 100, 0, 0, 910, 168, 1, 0, 0, 0, 911, 912, 5, 98, 0, 0, 912, 913, 5, 111, 0, 0, 913, 914, 5, 111, 0, 0, 914, 915, 5, 108, 0, 0, 915, 170, 1, 0, 0, 0, 916, 917, 5, 98, 0, 0, 917, 918, 5, 121, 0, 0, 918, 919, 5, 116, 0, 0, 919, 920, 5, 101, 0, 0, 920, 921, 5, 115, 0, 0, 921, 172, 1, 0, 0, 0, 922, 923, 5, 100, 0, 0, 923, 924, 5, 111, 0, 0, 924, 925, 5, 117, 0, 0, 925, 926, 5, 98, 0, 0, 926, 927, 5, 108, 0, 0, 927, 928, 5, 101, 0, 0, 928, 174, 1, 0, 0, 0, 929, 930, 5, 105, 0, 0, 930, 931, 5, 110, 0, 0, 931, 932, 5, 116, 0, 0, 932, 933, 5, 51, 0, 0, 933, 934, 5, 50, 0, 0, 934, 176, 1, 0, 0, 0, 935, 936, 5, 105, 0, 0, 936, 937, 5, 110, 0, 0, 937, 938, 5, 116, 0, 0, 938, 939, 5, 54, 0, 0, 939, 940, 5, 52, 0, 0, 940, 178, 1, 0, 0, 0, 941, 942, 5, 115, 0, 0, 942, 943, 5, 116, 0, 0, 943, 944, 5, 114, 0, 0, 944, 945, 5, 105, 0, 0, 945, 946, 5, 110, 0, 0, 946, 947, 5, 103, 0, 0, 947, 180, 1, 0, 0, 0, 948, 949, 5, 117, 0, 0, 949, 950, 5, 105, 0, 0, 950, 951, 5, 110, 0, 0, 951, 952, 5, 116, 0, 0, 952, 953, 5, 51, 0, 0, 953, 954, 5, 50, 0, 0, 954, 182, 1, 0, 0, 0, 955, 956, 5, 117, 0, 0, 956, 957, 5, 105, 0, 0, 957, 958, 5, 110, 0, 0, 958, 959, 5, 116, 0, 0, 959, 960, 5, 54, 0, 0, 960, 961, 5, 52, 0, 0, 961, 184, 1, 0, 0, 0, 962, 963, 5, 116, 0, 0, 963, 964, 5, 114, 0, 0, 964, 965, 5, 117, 0, 0, 965, 966, 5, 101, 0, 0, 966, 186, 1, 0, 0, 0, 967, 968, 5, 102, 0, 0, 968, 969, 5, 97, 0, 0, 969, 970, 5, 108, 0, 0, 970, 971, 5, 115, 0, 0, 971, 972, 5, 101, 0, 0, 972, 188, 1, 0, 0, 0, 973, 974, 5, 110, 0, 0, 974, 975, 5, 117, 0, 0, 975, 976, 5, 108, 0, 0, 976, 977, 5, 108, 0, 0, 977, 190, 1, 0, 0, 0, 978, 979, 5, 45, 0, 0, 979, 980, 5, 62, 0, 0, 980, 192, 1, 0, 0, 0, 981, 982, 5, 58, 0, 0, 982, 194, 1, 0, 0, 0, 983, 984, 5, 59, 0, 0, 984, 196, 1, 0, 0, 0, 985, 986, 5, 44, 0, 0, 986, 198, 1, 0, 0, 0, 987, 988, 5, 46, 0, 0, 988, 200, 1, 0, 0, 0, 989, 990, 5, 123, 0, 0, 990, 202, 1, 0, 0, 0, 991, 992, 5, 125, 0, 0, 992, 204, 1, 0, 0, 0, 993, 994, 5, 91, 0, 0, 994, 206, 1, 0, 0, 0, 995, 996, 5, 93, 0, 0, 996, 208, 1, 0, 0, 0, 997, 998, 5, 40, 0, 0, 998, 210, 1, 0, 0, 0, 999, 1000, 5, 41, 0, 0, 1000, 212, 1, 0, 0, 0, 1001, 1002, 5, 60, 0, 0, 1002, 214, 1, 0, 0, 0, 1003, 1004, 5, 62, 0, 0, 1004, 216, 1, 0, 0, 0, 1005, 1006, 5, 38, 0, 0, 1006, 218, 1, 0, 0, 0, 1007, 1008, 5, 61, 0, 0, 1008, 220, 1, 0, 0, 0, 1009, 1011, 5, 45, 0, 0, 1010, 1009, 1, 0, 0, 0, 1010, 1011, 1, 0, 0, 0, 1011, 1013, 1, 0, 0, 0, 1012, 1014, 7, 0, 0, 0, 1013, 1012, 1, 0, 0, 0, 1014, 1015, 1, 0, 0, 0, 1015, 1013, 1, 0, 0, 0, 1015, 1016, 1, 0, 0, 0, 1016, 222, 1, 0, 0, 0, 1017, 1019, 5, 45, 0, 0, 1018, 1017, 1, 0, 0, 0, 1018, 1019, 1, 0, 0, 0, 1019, 1028, 1, 0, 0, 0, 1020, 1029, 5, 48, 0, 0, 1021, 1025, 7, 1, 0, 0, 1022, 1024, 7, 0, 0, 0, 1023, 1022, 1, 0, 0, 0, 1024, 1027, 1, 0, 0, 0, 1025, 1023, 1, 0, 0, 0, 1025, 1026, 1, 0, 0, 0, 1026, 1029, 1, 0, 0, 0, 1027, 1025, 1, 0, 0, 0, 1028, 1020, 1, 0, 0, 0, 1028, 1021, 1, 0, 0, 0, 1029, 1036, 1, 0, 0, 0, 1030, 1032, 5, 46, 0, 0, 1031, 1033, 7, 0, 0, 0, 1032, 1031, 1, 0, 0, 0, 1033, 1034, 1, 0, 0, 0, 1034, 1032, 1, 0, 0, 0, 1034, 1035, 1, 0, 0, 0, 1035, 1037, 1, 0, 0, 0, 1036, 1030, 1, 0, 0, 0, 1036, 1037, 1, 0, 0, 0, 1037, 1047, 1, 0, 0, 0, 1038, 1040, 7, 2, 0, 0, 1039, 1041, 7, 3, 0, 0, 1040, 1039, 1, 0, 0, 0, 1040, 1041, 1, 0, 0, 0, 1041, 1043, 1, 0, 0, 0, 1042, 1044, 7, 0, 0, 0, 1043, 1042, 1, 0, 0, 0, 1044, 1045, 1, 0, 0, 0, 1045, 1043, 1, 0, 0, 0, 1045, 1046, 1, 0, 0, 0, 1046, 1048, 1, 0, 0, 0, 1047, 1038, 1, 0, 0, 0, 1047, 1048, 1, 0, 0, 0, 1048, 224, 1, 0, 0, 0, 1049, 1053, 7, 4, 0, 0, 1050, 1052, 7, 5, 0, 0, 1051, 1050, 1, 0, 0, 0, 1052, 1055, 1, 0, 0, 0, 1053, 1051, 1, 0, 0, 0, 1053, 1054, 1, 0, 0, 0, 1054, 226, 1, 0, 0, 0, 1055, 1053, 1, 0, 0, 0, 1056, 1061, 5, 34, 0, 0, 1057, 1060, 3, 229, 114, 0, 1058, 1060, 8, 6, 0, 0, 1059, 1057, 1, 0, 0, 0, 1059, 1058, 1, 0, 0, 0, 1060, 1063, 1, 0, 0, 0, 1061, 1059, 1, 0, 0, 0, 1061, 1062, 1, 0, 0, 0, 1062, 1064, 1, 0, 0, 0, 1063, 1061, 1, 0, 0, 0, 1064, 1065, 5, 34, 0, 0, 1065, 228, 1, 0, 0, 0, 1066, 1074, 5, 92, 0, 0, 1067, 1075, 7, 7, 0, 0, 1068, 1069, 5, 117, 0, 0, 1069, 1070, 3, 231, 115, 0, 1070, 1071, 3, 231, 115, 0, 1071, 1072, 3, 231, 115, 0, 1072, 1073, 3, 231, 115, 0, 1073, 1075, 1, 0, 0, 0, 1074, 1067, 1, 0, 0, 0, 1074, 1068, 1, 0, 0, 0, 1075, 230, 1, 0, 0, 0, 1076, 1077, 7, 8, 0, 0, 1077, 232, 1, 0, 0, 0, 1078, 1079, 5, 47, 0, 0, 1079, 1080, 5, 47, 0, 0, 1080, 1084, 1, 0, 0, 0, 1081, 1083, 8, 9, 0, 0, 1082, 1081, 1, 0, 0, 0, 1083, 1086, 1, 0, 0, 0, 1084, 1082, 1, 0, 0, 0, 1084, 1085, 1, 0, 0, 0, 1085, 1087, 1, 0, 0, 0, 1086, 1084, 1, 0, 0, 0, 1087, 1088, 6, 116, 0, 0, 1088, 234, 1, 0, 0, 0, 1089, 1090, 5, 47, 0, 0, 1090, 1091, 5, 42, 0, 0, 1091, 1095, 1, 0, 0, 0, 1092, 1094, 9, 0, 0, 0, 1093, 1092, 1, 0, 0, 0, 1094, 1097, 1, 0, 0, 0, 1095, 1096, 1, 0, 0, 0, 1095, 1093, 1, 0, 0, 0, 1096, 1098, 1, 0, 0, 0, 1097, 1095, 1, 0, 0, 0, 1098, 1099, 5, 42, 0, 0, 1099, 1100, 5, 47, 0, 0, 1100, 1101, 1, 0, 0, 0, 1101, 1102, 6, 117, 0, 0, 1102, 236, 1, 0, 0, 0, 1103, 1105, 7, 10, 0, 0, 1104, 1103, 1, 0, 0, 0, 1105, 1106, 1, 0, 0, 0, 1106, 1104, 1, 0, 0, 0, 1106, 1107, 1, 0, 0, 0, 1107, 1108, 1, 0, 0, 0, 1108, 1109, 6, 118, 0, 0, 1109, 238, 1, 0, 0, 0, 18, 0, 1010, 1015, 1018, 1025, 1028, 1034, 1036, 1040, 1045, 1047, 1053, 1059, 1061, 1074, 1084, 1095, 1106, 1, 0, 1, 0] \ No newline at end of file +[4, 0, 118, 1119, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 2, 88, 7, 88, 2, 89, 7, 89, 2, 90, 7, 90, 2, 91, 7, 91, 2, 92, 7, 92, 2, 93, 7, 93, 2, 94, 7, 94, 2, 95, 7, 95, 2, 96, 7, 96, 2, 97, 7, 97, 2, 98, 7, 98, 2, 99, 7, 99, 2, 100, 7, 100, 2, 101, 7, 101, 2, 102, 7, 102, 2, 103, 7, 103, 2, 104, 7, 104, 2, 105, 7, 105, 2, 106, 7, 106, 2, 107, 7, 107, 2, 108, 7, 108, 2, 109, 7, 109, 2, 110, 7, 110, 2, 111, 7, 111, 2, 112, 7, 112, 2, 113, 7, 113, 2, 114, 7, 114, 2, 115, 7, 115, 2, 116, 7, 116, 2, 117, 7, 117, 2, 118, 7, 118, 2, 119, 7, 119, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 83, 1, 83, 1, 83, 1, 83, 1, 83, 1, 84, 1, 84, 1, 84, 1, 84, 1, 84, 1, 84, 1, 84, 1, 85, 1, 85, 1, 85, 1, 85, 1, 85, 1, 86, 1, 86, 1, 86, 1, 86, 1, 86, 1, 86, 1, 87, 1, 87, 1, 87, 1, 87, 1, 87, 1, 87, 1, 87, 1, 88, 1, 88, 1, 88, 1, 88, 1, 88, 1, 88, 1, 89, 1, 89, 1, 89, 1, 89, 1, 89, 1, 89, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 91, 1, 91, 1, 91, 1, 91, 1, 91, 1, 91, 1, 91, 1, 92, 1, 92, 1, 92, 1, 92, 1, 92, 1, 92, 1, 92, 1, 93, 1, 93, 1, 93, 1, 93, 1, 93, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 96, 1, 96, 1, 96, 1, 97, 1, 97, 1, 98, 1, 98, 1, 99, 1, 99, 1, 100, 1, 100, 1, 101, 1, 101, 1, 102, 1, 102, 1, 103, 1, 103, 1, 104, 1, 104, 1, 105, 1, 105, 1, 106, 1, 106, 1, 107, 1, 107, 1, 108, 1, 108, 1, 109, 1, 109, 1, 110, 1, 110, 1, 111, 3, 111, 1020, 8, 111, 1, 111, 4, 111, 1023, 8, 111, 11, 111, 12, 111, 1024, 1, 112, 3, 112, 1028, 8, 112, 1, 112, 1, 112, 1, 112, 5, 112, 1033, 8, 112, 10, 112, 12, 112, 1036, 9, 112, 3, 112, 1038, 8, 112, 1, 112, 1, 112, 4, 112, 1042, 8, 112, 11, 112, 12, 112, 1043, 3, 112, 1046, 8, 112, 1, 112, 1, 112, 3, 112, 1050, 8, 112, 1, 112, 4, 112, 1053, 8, 112, 11, 112, 12, 112, 1054, 3, 112, 1057, 8, 112, 1, 113, 1, 113, 5, 113, 1061, 8, 113, 10, 113, 12, 113, 1064, 9, 113, 1, 114, 1, 114, 1, 114, 5, 114, 1069, 8, 114, 10, 114, 12, 114, 1072, 9, 114, 1, 114, 1, 114, 1, 115, 1, 115, 1, 115, 1, 115, 1, 115, 1, 115, 1, 115, 1, 115, 3, 115, 1084, 8, 115, 1, 116, 1, 116, 1, 117, 1, 117, 1, 117, 1, 117, 5, 117, 1092, 8, 117, 10, 117, 12, 117, 1095, 9, 117, 1, 117, 1, 117, 1, 118, 1, 118, 1, 118, 1, 118, 5, 118, 1103, 8, 118, 10, 118, 12, 118, 1106, 9, 118, 1, 118, 1, 118, 1, 118, 1, 118, 1, 118, 1, 119, 4, 119, 1114, 8, 119, 11, 119, 12, 119, 1115, 1, 119, 1, 119, 1, 1104, 0, 120, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 33, 67, 34, 69, 35, 71, 36, 73, 37, 75, 38, 77, 39, 79, 40, 81, 41, 83, 42, 85, 43, 87, 44, 89, 45, 91, 46, 93, 47, 95, 48, 97, 49, 99, 50, 101, 51, 103, 52, 105, 53, 107, 54, 109, 55, 111, 56, 113, 57, 115, 58, 117, 59, 119, 60, 121, 61, 123, 62, 125, 63, 127, 64, 129, 65, 131, 66, 133, 67, 135, 68, 137, 69, 139, 70, 141, 71, 143, 72, 145, 73, 147, 74, 149, 75, 151, 76, 153, 77, 155, 78, 157, 79, 159, 80, 161, 81, 163, 82, 165, 83, 167, 84, 169, 85, 171, 86, 173, 87, 175, 88, 177, 89, 179, 90, 181, 91, 183, 92, 185, 93, 187, 94, 189, 95, 191, 96, 193, 97, 195, 98, 197, 99, 199, 100, 201, 101, 203, 102, 205, 103, 207, 104, 209, 105, 211, 106, 213, 107, 215, 108, 217, 109, 219, 110, 221, 111, 223, 112, 225, 113, 227, 114, 229, 115, 231, 0, 233, 0, 235, 116, 237, 117, 239, 118, 1, 0, 11, 1, 0, 48, 57, 1, 0, 49, 57, 2, 0, 69, 69, 101, 101, 2, 0, 43, 43, 45, 45, 3, 0, 65, 90, 95, 95, 97, 122, 4, 0, 48, 57, 65, 90, 95, 95, 97, 122, 4, 0, 10, 10, 13, 13, 34, 34, 92, 92, 8, 0, 34, 34, 47, 47, 92, 92, 98, 98, 102, 102, 110, 110, 114, 114, 116, 116, 3, 0, 48, 57, 65, 70, 97, 102, 2, 0, 10, 10, 13, 13, 3, 0, 9, 10, 13, 13, 32, 32, 1133, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0, 65, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 73, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, 81, 1, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0, 89, 1, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 93, 1, 0, 0, 0, 0, 95, 1, 0, 0, 0, 0, 97, 1, 0, 0, 0, 0, 99, 1, 0, 0, 0, 0, 101, 1, 0, 0, 0, 0, 103, 1, 0, 0, 0, 0, 105, 1, 0, 0, 0, 0, 107, 1, 0, 0, 0, 0, 109, 1, 0, 0, 0, 0, 111, 1, 0, 0, 0, 0, 113, 1, 0, 0, 0, 0, 115, 1, 0, 0, 0, 0, 117, 1, 0, 0, 0, 0, 119, 1, 0, 0, 0, 0, 121, 1, 0, 0, 0, 0, 123, 1, 0, 0, 0, 0, 125, 1, 0, 0, 0, 0, 127, 1, 0, 0, 0, 0, 129, 1, 0, 0, 0, 0, 131, 1, 0, 0, 0, 0, 133, 1, 0, 0, 0, 0, 135, 1, 0, 0, 0, 0, 137, 1, 0, 0, 0, 0, 139, 1, 0, 0, 0, 0, 141, 1, 0, 0, 0, 0, 143, 1, 0, 0, 0, 0, 145, 1, 0, 0, 0, 0, 147, 1, 0, 0, 0, 0, 149, 1, 0, 0, 0, 0, 151, 1, 0, 0, 0, 0, 153, 1, 0, 0, 0, 0, 155, 1, 0, 0, 0, 0, 157, 1, 0, 0, 0, 0, 159, 1, 0, 0, 0, 0, 161, 1, 0, 0, 0, 0, 163, 1, 0, 0, 0, 0, 165, 1, 0, 0, 0, 0, 167, 1, 0, 0, 0, 0, 169, 1, 0, 0, 0, 0, 171, 1, 0, 0, 0, 0, 173, 1, 0, 0, 0, 0, 175, 1, 0, 0, 0, 0, 177, 1, 0, 0, 0, 0, 179, 1, 0, 0, 0, 0, 181, 1, 0, 0, 0, 0, 183, 1, 0, 0, 0, 0, 185, 1, 0, 0, 0, 0, 187, 1, 0, 0, 0, 0, 189, 1, 0, 0, 0, 0, 191, 1, 0, 0, 0, 0, 193, 1, 0, 0, 0, 0, 195, 1, 0, 0, 0, 0, 197, 1, 0, 0, 0, 0, 199, 1, 0, 0, 0, 0, 201, 1, 0, 0, 0, 0, 203, 1, 0, 0, 0, 0, 205, 1, 0, 0, 0, 0, 207, 1, 0, 0, 0, 0, 209, 1, 0, 0, 0, 0, 211, 1, 0, 0, 0, 0, 213, 1, 0, 0, 0, 0, 215, 1, 0, 0, 0, 0, 217, 1, 0, 0, 0, 0, 219, 1, 0, 0, 0, 0, 221, 1, 0, 0, 0, 0, 223, 1, 0, 0, 0, 0, 225, 1, 0, 0, 0, 0, 227, 1, 0, 0, 0, 0, 229, 1, 0, 0, 0, 0, 235, 1, 0, 0, 0, 0, 237, 1, 0, 0, 0, 0, 239, 1, 0, 0, 0, 1, 241, 1, 0, 0, 0, 3, 251, 1, 0, 0, 0, 5, 256, 1, 0, 0, 0, 7, 263, 1, 0, 0, 0, 9, 272, 1, 0, 0, 0, 11, 283, 1, 0, 0, 0, 13, 287, 1, 0, 0, 0, 15, 296, 1, 0, 0, 0, 17, 303, 1, 0, 0, 0, 19, 312, 1, 0, 0, 0, 21, 317, 1, 0, 0, 0, 23, 327, 1, 0, 0, 0, 25, 338, 1, 0, 0, 0, 27, 346, 1, 0, 0, 0, 29, 352, 1, 0, 0, 0, 31, 361, 1, 0, 0, 0, 33, 371, 1, 0, 0, 0, 35, 380, 1, 0, 0, 0, 37, 392, 1, 0, 0, 0, 39, 403, 1, 0, 0, 0, 41, 409, 1, 0, 0, 0, 43, 417, 1, 0, 0, 0, 45, 420, 1, 0, 0, 0, 47, 425, 1, 0, 0, 0, 49, 432, 1, 0, 0, 0, 51, 435, 1, 0, 0, 0, 53, 443, 1, 0, 0, 0, 55, 450, 1, 0, 0, 0, 57, 456, 1, 0, 0, 0, 59, 461, 1, 0, 0, 0, 61, 472, 1, 0, 0, 0, 63, 477, 1, 0, 0, 0, 65, 483, 1, 0, 0, 0, 67, 487, 1, 0, 0, 0, 69, 499, 1, 0, 0, 0, 71, 502, 1, 0, 0, 0, 73, 509, 1, 0, 0, 0, 75, 512, 1, 0, 0, 0, 77, 519, 1, 0, 0, 0, 79, 527, 1, 0, 0, 0, 81, 534, 1, 0, 0, 0, 83, 545, 1, 0, 0, 0, 85, 552, 1, 0, 0, 0, 87, 561, 1, 0, 0, 0, 89, 576, 1, 0, 0, 0, 91, 586, 1, 0, 0, 0, 93, 599, 1, 0, 0, 0, 95, 605, 1, 0, 0, 0, 97, 622, 1, 0, 0, 0, 99, 625, 1, 0, 0, 0, 101, 629, 1, 0, 0, 0, 103, 634, 1, 0, 0, 0, 105, 640, 1, 0, 0, 0, 107, 649, 1, 0, 0, 0, 109, 658, 1, 0, 0, 0, 111, 662, 1, 0, 0, 0, 113, 666, 1, 0, 0, 0, 115, 670, 1, 0, 0, 0, 117, 676, 1, 0, 0, 0, 119, 682, 1, 0, 0, 0, 121, 687, 1, 0, 0, 0, 123, 692, 1, 0, 0, 0, 125, 698, 1, 0, 0, 0, 127, 706, 1, 0, 0, 0, 129, 714, 1, 0, 0, 0, 131, 725, 1, 0, 0, 0, 133, 730, 1, 0, 0, 0, 135, 742, 1, 0, 0, 0, 137, 753, 1, 0, 0, 0, 139, 763, 1, 0, 0, 0, 141, 775, 1, 0, 0, 0, 143, 795, 1, 0, 0, 0, 145, 800, 1, 0, 0, 0, 147, 813, 1, 0, 0, 0, 149, 825, 1, 0, 0, 0, 151, 837, 1, 0, 0, 0, 153, 842, 1, 0, 0, 0, 155, 850, 1, 0, 0, 0, 157, 855, 1, 0, 0, 0, 159, 868, 1, 0, 0, 0, 161, 876, 1, 0, 0, 0, 163, 885, 1, 0, 0, 0, 165, 899, 1, 0, 0, 0, 167, 908, 1, 0, 0, 0, 169, 913, 1, 0, 0, 0, 171, 920, 1, 0, 0, 0, 173, 925, 1, 0, 0, 0, 175, 931, 1, 0, 0, 0, 177, 938, 1, 0, 0, 0, 179, 944, 1, 0, 0, 0, 181, 950, 1, 0, 0, 0, 183, 957, 1, 0, 0, 0, 185, 964, 1, 0, 0, 0, 187, 971, 1, 0, 0, 0, 189, 976, 1, 0, 0, 0, 191, 982, 1, 0, 0, 0, 193, 987, 1, 0, 0, 0, 195, 990, 1, 0, 0, 0, 197, 992, 1, 0, 0, 0, 199, 994, 1, 0, 0, 0, 201, 996, 1, 0, 0, 0, 203, 998, 1, 0, 0, 0, 205, 1000, 1, 0, 0, 0, 207, 1002, 1, 0, 0, 0, 209, 1004, 1, 0, 0, 0, 211, 1006, 1, 0, 0, 0, 213, 1008, 1, 0, 0, 0, 215, 1010, 1, 0, 0, 0, 217, 1012, 1, 0, 0, 0, 219, 1014, 1, 0, 0, 0, 221, 1016, 1, 0, 0, 0, 223, 1019, 1, 0, 0, 0, 225, 1027, 1, 0, 0, 0, 227, 1058, 1, 0, 0, 0, 229, 1065, 1, 0, 0, 0, 231, 1075, 1, 0, 0, 0, 233, 1085, 1, 0, 0, 0, 235, 1087, 1, 0, 0, 0, 237, 1098, 1, 0, 0, 0, 239, 1113, 1, 0, 0, 0, 241, 242, 5, 119, 0, 0, 242, 243, 5, 111, 0, 0, 243, 244, 5, 114, 0, 0, 244, 245, 5, 107, 0, 0, 245, 246, 5, 115, 0, 0, 246, 247, 5, 112, 0, 0, 247, 248, 5, 97, 0, 0, 248, 249, 5, 99, 0, 0, 249, 250, 5, 101, 0, 0, 250, 2, 1, 0, 0, 0, 251, 252, 5, 116, 0, 0, 252, 253, 5, 121, 0, 0, 253, 254, 5, 112, 0, 0, 254, 255, 5, 101, 0, 0, 255, 4, 1, 0, 0, 0, 256, 257, 5, 111, 0, 0, 257, 258, 5, 98, 0, 0, 258, 259, 5, 106, 0, 0, 259, 260, 5, 101, 0, 0, 260, 261, 5, 99, 0, 0, 261, 262, 5, 116, 0, 0, 262, 6, 1, 0, 0, 0, 263, 264, 5, 115, 0, 0, 264, 265, 5, 116, 0, 0, 265, 266, 5, 111, 0, 0, 266, 267, 5, 114, 0, 0, 267, 268, 5, 97, 0, 0, 268, 269, 5, 98, 0, 0, 269, 270, 5, 108, 0, 0, 270, 271, 5, 101, 0, 0, 271, 8, 1, 0, 0, 0, 272, 273, 5, 105, 0, 0, 273, 274, 5, 109, 0, 0, 274, 275, 5, 112, 0, 0, 275, 276, 5, 108, 0, 0, 276, 277, 5, 101, 0, 0, 277, 278, 5, 109, 0, 0, 278, 279, 5, 101, 0, 0, 279, 280, 5, 110, 0, 0, 280, 281, 5, 116, 0, 0, 281, 282, 5, 115, 0, 0, 282, 10, 1, 0, 0, 0, 283, 284, 5, 114, 0, 0, 284, 285, 5, 101, 0, 0, 285, 286, 5, 102, 0, 0, 286, 12, 1, 0, 0, 0, 287, 288, 5, 102, 0, 0, 288, 289, 5, 114, 0, 0, 289, 290, 5, 97, 0, 0, 290, 291, 5, 103, 0, 0, 291, 292, 5, 109, 0, 0, 292, 293, 5, 101, 0, 0, 293, 294, 5, 110, 0, 0, 294, 295, 5, 116, 0, 0, 295, 14, 1, 0, 0, 0, 296, 297, 5, 105, 0, 0, 297, 298, 5, 109, 0, 0, 298, 299, 5, 112, 0, 0, 299, 300, 5, 111, 0, 0, 300, 301, 5, 114, 0, 0, 301, 302, 5, 116, 0, 0, 302, 16, 1, 0, 0, 0, 303, 304, 5, 101, 0, 0, 304, 305, 5, 120, 0, 0, 305, 306, 5, 116, 0, 0, 306, 307, 5, 101, 0, 0, 307, 308, 5, 114, 0, 0, 308, 309, 5, 110, 0, 0, 309, 310, 5, 97, 0, 0, 310, 311, 5, 108, 0, 0, 311, 18, 1, 0, 0, 0, 312, 313, 5, 97, 0, 0, 313, 314, 5, 116, 0, 0, 314, 315, 5, 111, 0, 0, 315, 316, 5, 109, 0, 0, 316, 20, 1, 0, 0, 0, 317, 318, 5, 105, 0, 0, 318, 319, 5, 110, 0, 0, 319, 320, 5, 116, 0, 0, 320, 321, 5, 101, 0, 0, 321, 322, 5, 114, 0, 0, 322, 323, 5, 102, 0, 0, 323, 324, 5, 97, 0, 0, 324, 325, 5, 99, 0, 0, 325, 326, 5, 101, 0, 0, 326, 22, 1, 0, 0, 0, 327, 328, 5, 105, 0, 0, 328, 329, 5, 110, 0, 0, 329, 330, 5, 116, 0, 0, 330, 331, 5, 101, 0, 0, 331, 332, 5, 114, 0, 0, 332, 333, 5, 102, 0, 0, 333, 334, 5, 97, 0, 0, 334, 335, 5, 99, 0, 0, 335, 336, 5, 101, 0, 0, 336, 337, 5, 115, 0, 0, 337, 24, 1, 0, 0, 0, 338, 339, 5, 112, 0, 0, 339, 340, 5, 97, 0, 0, 340, 341, 5, 99, 0, 0, 341, 342, 5, 107, 0, 0, 342, 343, 5, 97, 0, 0, 343, 344, 5, 103, 0, 0, 344, 345, 5, 101, 0, 0, 345, 26, 1, 0, 0, 0, 346, 347, 5, 118, 0, 0, 347, 348, 5, 97, 0, 0, 348, 349, 5, 108, 0, 0, 349, 350, 5, 117, 0, 0, 350, 351, 5, 101, 0, 0, 351, 28, 1, 0, 0, 0, 352, 353, 5, 114, 0, 0, 353, 354, 5, 101, 0, 0, 354, 355, 5, 108, 0, 0, 355, 356, 5, 97, 0, 0, 356, 357, 5, 116, 0, 0, 357, 358, 5, 105, 0, 0, 358, 359, 5, 111, 0, 0, 359, 360, 5, 110, 0, 0, 360, 30, 1, 0, 0, 0, 361, 362, 5, 111, 0, 0, 362, 363, 5, 112, 0, 0, 363, 364, 5, 101, 0, 0, 364, 365, 5, 114, 0, 0, 365, 366, 5, 97, 0, 0, 366, 367, 5, 116, 0, 0, 367, 368, 5, 105, 0, 0, 368, 369, 5, 111, 0, 0, 369, 370, 5, 110, 0, 0, 370, 32, 1, 0, 0, 0, 371, 372, 5, 102, 0, 0, 372, 373, 5, 117, 0, 0, 373, 374, 5, 110, 0, 0, 374, 375, 5, 99, 0, 0, 375, 376, 5, 116, 0, 0, 376, 377, 5, 105, 0, 0, 377, 378, 5, 111, 0, 0, 378, 379, 5, 110, 0, 0, 379, 34, 1, 0, 0, 0, 380, 381, 5, 99, 0, 0, 381, 382, 5, 111, 0, 0, 382, 383, 5, 110, 0, 0, 383, 384, 5, 115, 0, 0, 384, 385, 5, 116, 0, 0, 385, 386, 5, 114, 0, 0, 386, 387, 5, 117, 0, 0, 387, 388, 5, 99, 0, 0, 388, 389, 5, 116, 0, 0, 389, 390, 5, 111, 0, 0, 390, 391, 5, 114, 0, 0, 391, 36, 1, 0, 0, 0, 392, 393, 5, 99, 0, 0, 393, 394, 5, 111, 0, 0, 394, 395, 5, 110, 0, 0, 395, 396, 5, 115, 0, 0, 396, 397, 5, 116, 0, 0, 397, 398, 5, 114, 0, 0, 398, 399, 5, 117, 0, 0, 399, 400, 5, 99, 0, 0, 400, 401, 5, 116, 0, 0, 401, 402, 5, 115, 0, 0, 402, 38, 1, 0, 0, 0, 403, 404, 5, 105, 0, 0, 404, 405, 5, 110, 0, 0, 405, 406, 5, 112, 0, 0, 406, 407, 5, 117, 0, 0, 407, 408, 5, 116, 0, 0, 408, 40, 1, 0, 0, 0, 409, 410, 5, 99, 0, 0, 410, 411, 5, 111, 0, 0, 411, 412, 5, 110, 0, 0, 412, 413, 5, 102, 0, 0, 413, 414, 5, 111, 0, 0, 414, 415, 5, 114, 0, 0, 415, 416, 5, 109, 0, 0, 416, 42, 1, 0, 0, 0, 417, 418, 5, 97, 0, 0, 418, 419, 5, 115, 0, 0, 419, 44, 1, 0, 0, 0, 420, 421, 5, 98, 0, 0, 421, 422, 5, 105, 0, 0, 422, 423, 5, 110, 0, 0, 423, 424, 5, 100, 0, 0, 424, 46, 1, 0, 0, 0, 425, 426, 5, 115, 0, 0, 426, 427, 5, 116, 0, 0, 427, 428, 5, 97, 0, 0, 428, 429, 5, 116, 0, 0, 429, 430, 5, 105, 0, 0, 430, 431, 5, 99, 0, 0, 431, 48, 1, 0, 0, 0, 432, 433, 5, 116, 0, 0, 433, 434, 5, 111, 0, 0, 434, 50, 1, 0, 0, 0, 435, 436, 5, 112, 0, 0, 436, 437, 5, 114, 0, 0, 437, 438, 5, 105, 0, 0, 438, 439, 5, 118, 0, 0, 439, 440, 5, 97, 0, 0, 440, 441, 5, 116, 0, 0, 441, 442, 5, 101, 0, 0, 442, 52, 1, 0, 0, 0, 443, 444, 5, 115, 0, 0, 444, 445, 5, 104, 0, 0, 445, 446, 5, 97, 0, 0, 446, 447, 5, 114, 0, 0, 447, 448, 5, 101, 0, 0, 448, 449, 5, 100, 0, 0, 449, 54, 1, 0, 0, 0, 450, 451, 5, 115, 0, 0, 451, 452, 5, 116, 0, 0, 452, 453, 5, 97, 0, 0, 453, 454, 5, 116, 0, 0, 454, 455, 5, 101, 0, 0, 455, 56, 1, 0, 0, 0, 456, 457, 5, 101, 0, 0, 457, 458, 5, 100, 0, 0, 458, 459, 5, 103, 0, 0, 459, 460, 5, 101, 0, 0, 460, 58, 1, 0, 0, 0, 461, 462, 5, 112, 0, 0, 462, 463, 5, 114, 0, 0, 463, 464, 5, 111, 0, 0, 464, 465, 5, 106, 0, 0, 465, 466, 5, 101, 0, 0, 466, 467, 5, 99, 0, 0, 467, 468, 5, 116, 0, 0, 468, 469, 5, 105, 0, 0, 469, 470, 5, 111, 0, 0, 470, 471, 5, 110, 0, 0, 471, 60, 1, 0, 0, 0, 472, 473, 5, 119, 0, 0, 473, 474, 5, 105, 0, 0, 474, 475, 5, 116, 0, 0, 475, 476, 5, 104, 0, 0, 476, 62, 1, 0, 0, 0, 477, 478, 5, 117, 0, 0, 478, 479, 5, 115, 0, 0, 479, 480, 5, 105, 0, 0, 480, 481, 5, 110, 0, 0, 481, 482, 5, 103, 0, 0, 482, 64, 1, 0, 0, 0, 483, 484, 5, 118, 0, 0, 484, 485, 5, 105, 0, 0, 485, 486, 5, 97, 0, 0, 486, 66, 1, 0, 0, 0, 487, 488, 5, 109, 0, 0, 488, 489, 5, 97, 0, 0, 489, 490, 5, 116, 0, 0, 490, 491, 5, 101, 0, 0, 491, 492, 5, 114, 0, 0, 492, 493, 5, 105, 0, 0, 493, 494, 5, 97, 0, 0, 494, 495, 5, 108, 0, 0, 495, 496, 5, 105, 0, 0, 496, 497, 5, 122, 0, 0, 497, 498, 5, 101, 0, 0, 498, 68, 1, 0, 0, 0, 499, 500, 5, 105, 0, 0, 500, 501, 5, 102, 0, 0, 501, 70, 1, 0, 0, 0, 502, 503, 5, 97, 0, 0, 503, 504, 5, 98, 0, 0, 504, 505, 5, 115, 0, 0, 505, 506, 5, 101, 0, 0, 506, 507, 5, 110, 0, 0, 507, 508, 5, 116, 0, 0, 508, 72, 1, 0, 0, 0, 509, 510, 5, 111, 0, 0, 510, 511, 5, 110, 0, 0, 511, 74, 1, 0, 0, 0, 512, 513, 5, 112, 0, 0, 513, 514, 5, 111, 0, 0, 514, 515, 5, 108, 0, 0, 515, 516, 5, 105, 0, 0, 516, 517, 5, 99, 0, 0, 517, 518, 5, 121, 0, 0, 518, 76, 1, 0, 0, 0, 519, 520, 5, 100, 0, 0, 520, 521, 5, 101, 0, 0, 521, 522, 5, 102, 0, 0, 522, 523, 5, 97, 0, 0, 523, 524, 5, 117, 0, 0, 524, 525, 5, 108, 0, 0, 525, 526, 5, 116, 0, 0, 526, 78, 1, 0, 0, 0, 527, 528, 5, 115, 0, 0, 528, 529, 5, 111, 0, 0, 529, 530, 5, 117, 0, 0, 530, 531, 5, 114, 0, 0, 531, 532, 5, 99, 0, 0, 532, 533, 5, 101, 0, 0, 533, 80, 1, 0, 0, 0, 534, 535, 5, 114, 0, 0, 535, 536, 5, 101, 0, 0, 536, 537, 5, 112, 0, 0, 537, 538, 5, 111, 0, 0, 538, 539, 5, 115, 0, 0, 539, 540, 5, 105, 0, 0, 540, 541, 5, 116, 0, 0, 541, 542, 5, 111, 0, 0, 542, 543, 5, 114, 0, 0, 543, 544, 5, 121, 0, 0, 544, 82, 1, 0, 0, 0, 545, 546, 5, 99, 0, 0, 546, 547, 5, 111, 0, 0, 547, 548, 5, 109, 0, 0, 548, 549, 5, 109, 0, 0, 549, 550, 5, 105, 0, 0, 550, 551, 5, 116, 0, 0, 551, 84, 1, 0, 0, 0, 552, 553, 5, 114, 0, 0, 553, 554, 5, 101, 0, 0, 554, 555, 5, 118, 0, 0, 555, 556, 5, 105, 0, 0, 556, 557, 5, 115, 0, 0, 557, 558, 5, 105, 0, 0, 558, 559, 5, 111, 0, 0, 559, 560, 5, 110, 0, 0, 560, 86, 1, 0, 0, 0, 561, 562, 5, 115, 0, 0, 562, 563, 5, 101, 0, 0, 563, 564, 5, 109, 0, 0, 564, 565, 5, 97, 0, 0, 565, 566, 5, 110, 0, 0, 566, 567, 5, 116, 0, 0, 567, 568, 5, 105, 0, 0, 568, 569, 5, 99, 0, 0, 569, 570, 5, 45, 0, 0, 570, 571, 5, 109, 0, 0, 571, 572, 5, 97, 0, 0, 572, 573, 5, 106, 0, 0, 573, 574, 5, 111, 0, 0, 574, 575, 5, 114, 0, 0, 575, 88, 1, 0, 0, 0, 576, 577, 5, 111, 0, 0, 577, 578, 5, 110, 0, 0, 578, 579, 5, 45, 0, 0, 579, 580, 5, 100, 0, 0, 580, 581, 5, 101, 0, 0, 581, 582, 5, 108, 0, 0, 582, 583, 5, 101, 0, 0, 583, 584, 5, 116, 0, 0, 584, 585, 5, 101, 0, 0, 585, 90, 1, 0, 0, 0, 586, 587, 5, 114, 0, 0, 587, 588, 5, 101, 0, 0, 588, 589, 5, 116, 0, 0, 589, 590, 5, 97, 0, 0, 590, 591, 5, 105, 0, 0, 591, 592, 5, 110, 0, 0, 592, 593, 5, 45, 0, 0, 593, 594, 5, 111, 0, 0, 594, 595, 5, 116, 0, 0, 595, 596, 5, 104, 0, 0, 596, 597, 5, 101, 0, 0, 597, 598, 5, 114, 0, 0, 598, 92, 1, 0, 0, 0, 599, 600, 5, 107, 0, 0, 600, 601, 5, 101, 0, 0, 601, 602, 5, 121, 0, 0, 602, 603, 5, 101, 0, 0, 603, 604, 5, 100, 0, 0, 604, 94, 1, 0, 0, 0, 605, 606, 5, 112, 0, 0, 606, 607, 5, 117, 0, 0, 607, 608, 5, 98, 0, 0, 608, 609, 5, 108, 0, 0, 609, 610, 5, 105, 0, 0, 610, 611, 5, 99, 0, 0, 611, 612, 5, 45, 0, 0, 612, 613, 5, 116, 0, 0, 613, 614, 5, 114, 0, 0, 614, 615, 5, 97, 0, 0, 615, 616, 5, 118, 0, 0, 616, 617, 5, 101, 0, 0, 617, 618, 5, 114, 0, 0, 618, 619, 5, 115, 0, 0, 619, 620, 5, 97, 0, 0, 620, 621, 5, 108, 0, 0, 621, 96, 1, 0, 0, 0, 622, 623, 5, 105, 0, 0, 623, 624, 5, 100, 0, 0, 624, 98, 1, 0, 0, 0, 625, 626, 5, 100, 0, 0, 626, 627, 5, 111, 0, 0, 627, 628, 5, 99, 0, 0, 628, 100, 1, 0, 0, 0, 629, 630, 5, 109, 0, 0, 630, 631, 5, 111, 0, 0, 631, 632, 5, 100, 0, 0, 632, 633, 5, 101, 0, 0, 633, 102, 1, 0, 0, 0, 634, 635, 5, 101, 0, 0, 635, 636, 5, 109, 0, 0, 636, 637, 5, 105, 0, 0, 637, 638, 5, 116, 0, 0, 638, 639, 5, 115, 0, 0, 639, 104, 1, 0, 0, 0, 640, 641, 5, 114, 0, 0, 641, 642, 5, 101, 0, 0, 642, 643, 5, 99, 0, 0, 643, 644, 5, 101, 0, 0, 644, 645, 5, 105, 0, 0, 645, 646, 5, 118, 0, 0, 646, 647, 5, 101, 0, 0, 647, 648, 5, 114, 0, 0, 648, 106, 1, 0, 0, 0, 649, 650, 5, 114, 0, 0, 650, 651, 5, 101, 0, 0, 651, 652, 5, 113, 0, 0, 652, 653, 5, 117, 0, 0, 653, 654, 5, 105, 0, 0, 654, 655, 5, 114, 0, 0, 655, 656, 5, 101, 0, 0, 656, 657, 5, 115, 0, 0, 657, 108, 1, 0, 0, 0, 658, 659, 5, 97, 0, 0, 659, 660, 5, 110, 0, 0, 660, 661, 5, 121, 0, 0, 661, 110, 1, 0, 0, 0, 662, 663, 5, 103, 0, 0, 663, 664, 5, 101, 0, 0, 664, 665, 5, 116, 0, 0, 665, 112, 1, 0, 0, 0, 666, 667, 5, 115, 0, 0, 667, 668, 5, 101, 0, 0, 668, 669, 5, 116, 0, 0, 669, 114, 1, 0, 0, 0, 670, 671, 5, 119, 0, 0, 671, 672, 5, 97, 0, 0, 672, 673, 5, 116, 0, 0, 673, 674, 5, 99, 0, 0, 674, 675, 5, 104, 0, 0, 675, 116, 1, 0, 0, 0, 676, 677, 5, 115, 0, 0, 677, 678, 5, 116, 0, 0, 678, 679, 5, 97, 0, 0, 679, 680, 5, 114, 0, 0, 680, 681, 5, 116, 0, 0, 681, 118, 1, 0, 0, 0, 682, 683, 5, 115, 0, 0, 683, 684, 5, 116, 0, 0, 684, 685, 5, 111, 0, 0, 685, 686, 5, 112, 0, 0, 686, 120, 1, 0, 0, 0, 687, 688, 5, 114, 0, 0, 688, 689, 5, 101, 0, 0, 689, 690, 5, 97, 0, 0, 690, 691, 5, 100, 0, 0, 691, 122, 1, 0, 0, 0, 692, 693, 5, 119, 0, 0, 693, 694, 5, 114, 0, 0, 694, 695, 5, 105, 0, 0, 695, 696, 5, 116, 0, 0, 696, 697, 5, 101, 0, 0, 697, 124, 1, 0, 0, 0, 698, 699, 5, 114, 0, 0, 699, 700, 5, 101, 0, 0, 700, 701, 5, 115, 0, 0, 701, 702, 5, 111, 0, 0, 702, 703, 5, 108, 0, 0, 703, 704, 5, 118, 0, 0, 704, 705, 5, 101, 0, 0, 705, 126, 1, 0, 0, 0, 706, 707, 5, 99, 0, 0, 707, 708, 5, 111, 0, 0, 708, 709, 5, 110, 0, 0, 709, 710, 5, 110, 0, 0, 710, 711, 5, 101, 0, 0, 711, 712, 5, 99, 0, 0, 712, 713, 5, 116, 0, 0, 713, 128, 1, 0, 0, 0, 714, 715, 5, 100, 0, 0, 715, 716, 5, 105, 0, 0, 716, 717, 5, 115, 0, 0, 717, 718, 5, 99, 0, 0, 718, 719, 5, 111, 0, 0, 719, 720, 5, 110, 0, 0, 720, 721, 5, 110, 0, 0, 721, 722, 5, 101, 0, 0, 722, 723, 5, 99, 0, 0, 723, 724, 5, 116, 0, 0, 724, 130, 1, 0, 0, 0, 725, 726, 5, 99, 0, 0, 726, 727, 5, 97, 0, 0, 727, 728, 5, 108, 0, 0, 728, 729, 5, 108, 0, 0, 729, 132, 1, 0, 0, 0, 730, 731, 5, 119, 0, 0, 731, 732, 5, 97, 0, 0, 732, 733, 5, 116, 0, 0, 733, 734, 5, 99, 0, 0, 734, 735, 5, 104, 0, 0, 735, 736, 5, 45, 0, 0, 736, 737, 5, 115, 0, 0, 737, 738, 5, 116, 0, 0, 738, 739, 5, 97, 0, 0, 739, 740, 5, 114, 0, 0, 740, 741, 5, 116, 0, 0, 741, 134, 1, 0, 0, 0, 742, 743, 5, 119, 0, 0, 743, 744, 5, 97, 0, 0, 744, 745, 5, 116, 0, 0, 745, 746, 5, 99, 0, 0, 746, 747, 5, 104, 0, 0, 747, 748, 5, 45, 0, 0, 748, 749, 5, 115, 0, 0, 749, 750, 5, 116, 0, 0, 750, 751, 5, 111, 0, 0, 751, 752, 5, 112, 0, 0, 752, 136, 1, 0, 0, 0, 753, 754, 5, 115, 0, 0, 754, 755, 5, 117, 0, 0, 755, 756, 5, 98, 0, 0, 756, 757, 5, 115, 0, 0, 757, 758, 5, 99, 0, 0, 758, 759, 5, 114, 0, 0, 759, 760, 5, 105, 0, 0, 760, 761, 5, 98, 0, 0, 761, 762, 5, 101, 0, 0, 762, 138, 1, 0, 0, 0, 763, 764, 5, 117, 0, 0, 764, 765, 5, 110, 0, 0, 765, 766, 5, 115, 0, 0, 766, 767, 5, 117, 0, 0, 767, 768, 5, 98, 0, 0, 768, 769, 5, 115, 0, 0, 769, 770, 5, 99, 0, 0, 770, 771, 5, 114, 0, 0, 771, 772, 5, 105, 0, 0, 772, 773, 5, 98, 0, 0, 773, 774, 5, 101, 0, 0, 774, 140, 1, 0, 0, 0, 775, 776, 5, 111, 0, 0, 776, 777, 5, 112, 0, 0, 777, 778, 5, 116, 0, 0, 778, 779, 5, 105, 0, 0, 779, 780, 5, 109, 0, 0, 780, 781, 5, 105, 0, 0, 781, 782, 5, 115, 0, 0, 782, 783, 5, 116, 0, 0, 783, 784, 5, 105, 0, 0, 784, 785, 5, 99, 0, 0, 785, 786, 5, 45, 0, 0, 786, 787, 5, 114, 0, 0, 787, 788, 5, 101, 0, 0, 788, 789, 5, 103, 0, 0, 789, 790, 5, 105, 0, 0, 790, 791, 5, 115, 0, 0, 791, 792, 5, 116, 0, 0, 792, 793, 5, 101, 0, 0, 793, 794, 5, 114, 0, 0, 794, 142, 1, 0, 0, 0, 795, 796, 5, 99, 0, 0, 796, 797, 5, 114, 0, 0, 797, 798, 5, 100, 0, 0, 798, 799, 5, 116, 0, 0, 799, 144, 1, 0, 0, 0, 800, 801, 5, 111, 0, 0, 801, 802, 5, 112, 0, 0, 802, 803, 5, 116, 0, 0, 803, 804, 5, 105, 0, 0, 804, 805, 5, 111, 0, 0, 805, 806, 5, 110, 0, 0, 806, 807, 5, 97, 0, 0, 807, 808, 5, 108, 0, 0, 808, 809, 5, 45, 0, 0, 809, 810, 5, 111, 0, 0, 810, 811, 5, 110, 0, 0, 811, 812, 5, 101, 0, 0, 812, 146, 1, 0, 0, 0, 813, 814, 5, 101, 0, 0, 814, 815, 5, 120, 0, 0, 815, 816, 5, 97, 0, 0, 816, 817, 5, 99, 0, 0, 817, 818, 5, 116, 0, 0, 818, 819, 5, 108, 0, 0, 819, 820, 5, 121, 0, 0, 820, 821, 5, 45, 0, 0, 821, 822, 5, 111, 0, 0, 822, 823, 5, 110, 0, 0, 823, 824, 5, 101, 0, 0, 824, 148, 1, 0, 0, 0, 825, 826, 5, 109, 0, 0, 826, 827, 5, 97, 0, 0, 827, 828, 5, 110, 0, 0, 828, 829, 5, 121, 0, 0, 829, 830, 5, 45, 0, 0, 830, 831, 5, 117, 0, 0, 831, 832, 5, 110, 0, 0, 832, 833, 5, 105, 0, 0, 833, 834, 5, 113, 0, 0, 834, 835, 5, 117, 0, 0, 835, 836, 5, 101, 0, 0, 836, 150, 1, 0, 0, 0, 837, 838, 5, 109, 0, 0, 838, 839, 5, 97, 0, 0, 839, 840, 5, 110, 0, 0, 840, 841, 5, 121, 0, 0, 841, 152, 1, 0, 0, 0, 842, 843, 5, 111, 0, 0, 843, 844, 5, 114, 0, 0, 844, 845, 5, 100, 0, 0, 845, 846, 5, 101, 0, 0, 846, 847, 5, 114, 0, 0, 847, 848, 5, 101, 0, 0, 848, 849, 5, 100, 0, 0, 849, 154, 1, 0, 0, 0, 850, 851, 5, 117, 0, 0, 851, 852, 5, 110, 0, 0, 852, 853, 5, 105, 0, 0, 853, 854, 5, 116, 0, 0, 854, 156, 1, 0, 0, 0, 855, 856, 5, 119, 0, 0, 856, 857, 5, 97, 0, 0, 857, 858, 5, 116, 0, 0, 858, 859, 5, 99, 0, 0, 859, 860, 5, 104, 0, 0, 860, 861, 5, 45, 0, 0, 861, 862, 5, 104, 0, 0, 862, 863, 5, 97, 0, 0, 863, 864, 5, 110, 0, 0, 864, 865, 5, 100, 0, 0, 865, 866, 5, 108, 0, 0, 866, 867, 5, 101, 0, 0, 867, 158, 1, 0, 0, 0, 868, 869, 5, 109, 0, 0, 869, 870, 5, 101, 0, 0, 870, 871, 5, 115, 0, 0, 871, 872, 5, 115, 0, 0, 872, 873, 5, 97, 0, 0, 873, 874, 5, 103, 0, 0, 874, 875, 5, 101, 0, 0, 875, 160, 1, 0, 0, 0, 876, 877, 5, 97, 0, 0, 877, 878, 5, 116, 0, 0, 878, 879, 5, 111, 0, 0, 879, 880, 5, 109, 0, 0, 880, 881, 5, 45, 0, 0, 881, 882, 5, 114, 0, 0, 882, 883, 5, 101, 0, 0, 883, 884, 5, 102, 0, 0, 884, 162, 1, 0, 0, 0, 885, 886, 5, 105, 0, 0, 886, 887, 5, 110, 0, 0, 887, 888, 5, 116, 0, 0, 888, 889, 5, 101, 0, 0, 889, 890, 5, 114, 0, 0, 890, 891, 5, 102, 0, 0, 891, 892, 5, 97, 0, 0, 892, 893, 5, 99, 0, 0, 893, 894, 5, 101, 0, 0, 894, 895, 5, 45, 0, 0, 895, 896, 5, 114, 0, 0, 896, 897, 5, 101, 0, 0, 897, 898, 5, 102, 0, 0, 898, 164, 1, 0, 0, 0, 899, 900, 5, 111, 0, 0, 900, 901, 5, 112, 0, 0, 901, 902, 5, 116, 0, 0, 902, 903, 5, 105, 0, 0, 903, 904, 5, 111, 0, 0, 904, 905, 5, 110, 0, 0, 905, 906, 5, 97, 0, 0, 906, 907, 5, 108, 0, 0, 907, 166, 1, 0, 0, 0, 908, 909, 5, 108, 0, 0, 909, 910, 5, 105, 0, 0, 910, 911, 5, 115, 0, 0, 911, 912, 5, 116, 0, 0, 912, 168, 1, 0, 0, 0, 913, 914, 5, 114, 0, 0, 914, 915, 5, 101, 0, 0, 915, 916, 5, 99, 0, 0, 916, 917, 5, 111, 0, 0, 917, 918, 5, 114, 0, 0, 918, 919, 5, 100, 0, 0, 919, 170, 1, 0, 0, 0, 920, 921, 5, 98, 0, 0, 921, 922, 5, 111, 0, 0, 922, 923, 5, 111, 0, 0, 923, 924, 5, 108, 0, 0, 924, 172, 1, 0, 0, 0, 925, 926, 5, 98, 0, 0, 926, 927, 5, 121, 0, 0, 927, 928, 5, 116, 0, 0, 928, 929, 5, 101, 0, 0, 929, 930, 5, 115, 0, 0, 930, 174, 1, 0, 0, 0, 931, 932, 5, 100, 0, 0, 932, 933, 5, 111, 0, 0, 933, 934, 5, 117, 0, 0, 934, 935, 5, 98, 0, 0, 935, 936, 5, 108, 0, 0, 936, 937, 5, 101, 0, 0, 937, 176, 1, 0, 0, 0, 938, 939, 5, 105, 0, 0, 939, 940, 5, 110, 0, 0, 940, 941, 5, 116, 0, 0, 941, 942, 5, 51, 0, 0, 942, 943, 5, 50, 0, 0, 943, 178, 1, 0, 0, 0, 944, 945, 5, 105, 0, 0, 945, 946, 5, 110, 0, 0, 946, 947, 5, 116, 0, 0, 947, 948, 5, 54, 0, 0, 948, 949, 5, 52, 0, 0, 949, 180, 1, 0, 0, 0, 950, 951, 5, 115, 0, 0, 951, 952, 5, 116, 0, 0, 952, 953, 5, 114, 0, 0, 953, 954, 5, 105, 0, 0, 954, 955, 5, 110, 0, 0, 955, 956, 5, 103, 0, 0, 956, 182, 1, 0, 0, 0, 957, 958, 5, 117, 0, 0, 958, 959, 5, 105, 0, 0, 959, 960, 5, 110, 0, 0, 960, 961, 5, 116, 0, 0, 961, 962, 5, 51, 0, 0, 962, 963, 5, 50, 0, 0, 963, 184, 1, 0, 0, 0, 964, 965, 5, 117, 0, 0, 965, 966, 5, 105, 0, 0, 966, 967, 5, 110, 0, 0, 967, 968, 5, 116, 0, 0, 968, 969, 5, 54, 0, 0, 969, 970, 5, 52, 0, 0, 970, 186, 1, 0, 0, 0, 971, 972, 5, 116, 0, 0, 972, 973, 5, 114, 0, 0, 973, 974, 5, 117, 0, 0, 974, 975, 5, 101, 0, 0, 975, 188, 1, 0, 0, 0, 976, 977, 5, 102, 0, 0, 977, 978, 5, 97, 0, 0, 978, 979, 5, 108, 0, 0, 979, 980, 5, 115, 0, 0, 980, 981, 5, 101, 0, 0, 981, 190, 1, 0, 0, 0, 982, 983, 5, 110, 0, 0, 983, 984, 5, 117, 0, 0, 984, 985, 5, 108, 0, 0, 985, 986, 5, 108, 0, 0, 986, 192, 1, 0, 0, 0, 987, 988, 5, 45, 0, 0, 988, 989, 5, 62, 0, 0, 989, 194, 1, 0, 0, 0, 990, 991, 5, 58, 0, 0, 991, 196, 1, 0, 0, 0, 992, 993, 5, 59, 0, 0, 993, 198, 1, 0, 0, 0, 994, 995, 5, 44, 0, 0, 995, 200, 1, 0, 0, 0, 996, 997, 5, 46, 0, 0, 997, 202, 1, 0, 0, 0, 998, 999, 5, 123, 0, 0, 999, 204, 1, 0, 0, 0, 1000, 1001, 5, 125, 0, 0, 1001, 206, 1, 0, 0, 0, 1002, 1003, 5, 91, 0, 0, 1003, 208, 1, 0, 0, 0, 1004, 1005, 5, 93, 0, 0, 1005, 210, 1, 0, 0, 0, 1006, 1007, 5, 40, 0, 0, 1007, 212, 1, 0, 0, 0, 1008, 1009, 5, 41, 0, 0, 1009, 214, 1, 0, 0, 0, 1010, 1011, 5, 60, 0, 0, 1011, 216, 1, 0, 0, 0, 1012, 1013, 5, 62, 0, 0, 1013, 218, 1, 0, 0, 0, 1014, 1015, 5, 38, 0, 0, 1015, 220, 1, 0, 0, 0, 1016, 1017, 5, 61, 0, 0, 1017, 222, 1, 0, 0, 0, 1018, 1020, 5, 45, 0, 0, 1019, 1018, 1, 0, 0, 0, 1019, 1020, 1, 0, 0, 0, 1020, 1022, 1, 0, 0, 0, 1021, 1023, 7, 0, 0, 0, 1022, 1021, 1, 0, 0, 0, 1023, 1024, 1, 0, 0, 0, 1024, 1022, 1, 0, 0, 0, 1024, 1025, 1, 0, 0, 0, 1025, 224, 1, 0, 0, 0, 1026, 1028, 5, 45, 0, 0, 1027, 1026, 1, 0, 0, 0, 1027, 1028, 1, 0, 0, 0, 1028, 1037, 1, 0, 0, 0, 1029, 1038, 5, 48, 0, 0, 1030, 1034, 7, 1, 0, 0, 1031, 1033, 7, 0, 0, 0, 1032, 1031, 1, 0, 0, 0, 1033, 1036, 1, 0, 0, 0, 1034, 1032, 1, 0, 0, 0, 1034, 1035, 1, 0, 0, 0, 1035, 1038, 1, 0, 0, 0, 1036, 1034, 1, 0, 0, 0, 1037, 1029, 1, 0, 0, 0, 1037, 1030, 1, 0, 0, 0, 1038, 1045, 1, 0, 0, 0, 1039, 1041, 5, 46, 0, 0, 1040, 1042, 7, 0, 0, 0, 1041, 1040, 1, 0, 0, 0, 1042, 1043, 1, 0, 0, 0, 1043, 1041, 1, 0, 0, 0, 1043, 1044, 1, 0, 0, 0, 1044, 1046, 1, 0, 0, 0, 1045, 1039, 1, 0, 0, 0, 1045, 1046, 1, 0, 0, 0, 1046, 1056, 1, 0, 0, 0, 1047, 1049, 7, 2, 0, 0, 1048, 1050, 7, 3, 0, 0, 1049, 1048, 1, 0, 0, 0, 1049, 1050, 1, 0, 0, 0, 1050, 1052, 1, 0, 0, 0, 1051, 1053, 7, 0, 0, 0, 1052, 1051, 1, 0, 0, 0, 1053, 1054, 1, 0, 0, 0, 1054, 1052, 1, 0, 0, 0, 1054, 1055, 1, 0, 0, 0, 1055, 1057, 1, 0, 0, 0, 1056, 1047, 1, 0, 0, 0, 1056, 1057, 1, 0, 0, 0, 1057, 226, 1, 0, 0, 0, 1058, 1062, 7, 4, 0, 0, 1059, 1061, 7, 5, 0, 0, 1060, 1059, 1, 0, 0, 0, 1061, 1064, 1, 0, 0, 0, 1062, 1060, 1, 0, 0, 0, 1062, 1063, 1, 0, 0, 0, 1063, 228, 1, 0, 0, 0, 1064, 1062, 1, 0, 0, 0, 1065, 1070, 5, 34, 0, 0, 1066, 1069, 3, 231, 115, 0, 1067, 1069, 8, 6, 0, 0, 1068, 1066, 1, 0, 0, 0, 1068, 1067, 1, 0, 0, 0, 1069, 1072, 1, 0, 0, 0, 1070, 1068, 1, 0, 0, 0, 1070, 1071, 1, 0, 0, 0, 1071, 1073, 1, 0, 0, 0, 1072, 1070, 1, 0, 0, 0, 1073, 1074, 5, 34, 0, 0, 1074, 230, 1, 0, 0, 0, 1075, 1083, 5, 92, 0, 0, 1076, 1084, 7, 7, 0, 0, 1077, 1078, 5, 117, 0, 0, 1078, 1079, 3, 233, 116, 0, 1079, 1080, 3, 233, 116, 0, 1080, 1081, 3, 233, 116, 0, 1081, 1082, 3, 233, 116, 0, 1082, 1084, 1, 0, 0, 0, 1083, 1076, 1, 0, 0, 0, 1083, 1077, 1, 0, 0, 0, 1084, 232, 1, 0, 0, 0, 1085, 1086, 7, 8, 0, 0, 1086, 234, 1, 0, 0, 0, 1087, 1088, 5, 47, 0, 0, 1088, 1089, 5, 47, 0, 0, 1089, 1093, 1, 0, 0, 0, 1090, 1092, 8, 9, 0, 0, 1091, 1090, 1, 0, 0, 0, 1092, 1095, 1, 0, 0, 0, 1093, 1091, 1, 0, 0, 0, 1093, 1094, 1, 0, 0, 0, 1094, 1096, 1, 0, 0, 0, 1095, 1093, 1, 0, 0, 0, 1096, 1097, 6, 117, 0, 0, 1097, 236, 1, 0, 0, 0, 1098, 1099, 5, 47, 0, 0, 1099, 1100, 5, 42, 0, 0, 1100, 1104, 1, 0, 0, 0, 1101, 1103, 9, 0, 0, 0, 1102, 1101, 1, 0, 0, 0, 1103, 1106, 1, 0, 0, 0, 1104, 1105, 1, 0, 0, 0, 1104, 1102, 1, 0, 0, 0, 1105, 1107, 1, 0, 0, 0, 1106, 1104, 1, 0, 0, 0, 1107, 1108, 5, 42, 0, 0, 1108, 1109, 5, 47, 0, 0, 1109, 1110, 1, 0, 0, 0, 1110, 1111, 6, 118, 0, 0, 1111, 238, 1, 0, 0, 0, 1112, 1114, 7, 10, 0, 0, 1113, 1112, 1, 0, 0, 0, 1114, 1115, 1, 0, 0, 0, 1115, 1113, 1, 0, 0, 0, 1115, 1116, 1, 0, 0, 0, 1116, 1117, 1, 0, 0, 0, 1117, 1118, 6, 119, 0, 0, 1118, 240, 1, 0, 0, 0, 18, 0, 1019, 1024, 1027, 1034, 1037, 1043, 1045, 1049, 1054, 1056, 1062, 1068, 1070, 1083, 1093, 1104, 1115, 1, 0, 1, 0] \ No newline at end of file diff --git a/src/capability-language/generated/QuixosCapabilityLexer.tokens b/src/capability-language/generated/QuixosCapabilityLexer.tokens index 75f42fb..ed12769 100644 --- a/src/capability-language/generated/QuixosCapabilityLexer.tokens +++ b/src/capability-language/generated/QuixosCapabilityLexer.tokens @@ -21,100 +21,101 @@ INPUT=20 CONFORM=21 AS=22 BIND=23 -TO=24 -PRIVATE=25 -SHARED=26 -STATE=27 -EDGE=28 -PROJECTION=29 -WITH=30 -USING=31 -VIA=32 -MATERIALIZE=33 -IF=34 -ABSENT=35 -ON=36 -POLICY=37 -DEFAULT=38 -SOURCE=39 -REPOSITORY=40 -COMMIT=41 -REVISION=42 -SEMANTIC_MAJOR=43 -ON_DELETE=44 -RETAIN_OTHER=45 -KEYED=46 -PUBLIC_TRAVERSAL=47 -ID=48 -DOC=49 -MODE=50 -EMITS=51 -RECEIVER=52 -REQUIRES=53 -ANY=54 -GET=55 -SET=56 -WATCH=57 -START=58 -STOP=59 -READ=60 -WRITE=61 -RESOLVE=62 -CONNECT=63 -DISCONNECT=64 -CALL=65 -WATCH_START=66 -WATCH_STOP=67 -SUBSCRIBE=68 -UNSUBSCRIBE=69 -OPTIMISTIC_REGISTER=70 -CRDT=71 -OPTIONAL_ONE=72 -EXACTLY_ONE=73 -MANY_UNIQUE=74 -MANY=75 -ORDERED=76 -UNIT=77 -WATCH_HANDLE=78 -MESSAGE=79 -ATOM_REF=80 -INTERFACE_REF=81 -OPTIONAL=82 -LIST=83 -RECORD=84 -BOOL=85 -BYTES=86 -DOUBLE=87 -INT32=88 -INT64=89 -STRING=90 -UINT32=91 -UINT64=92 -TRUE=93 -FALSE=94 -NULL=95 -ARROW=96 -COLON=97 -SEMI=98 -COMMA=99 -DOT=100 -LBRACE=101 -RBRACE=102 -LBRACK=103 -RBRACK=104 -LPAREN=105 -RPAREN=106 -LT=107 -GT=108 -AMP=109 -EQUAL=110 -INTEGER=111 -JSON_NUMBER=112 -IDENTIFIER=113 -STRING_LITERAL=114 -LINE_COMMENT=115 -BLOCK_COMMENT=116 -WS=117 +STATIC=24 +TO=25 +PRIVATE=26 +SHARED=27 +STATE=28 +EDGE=29 +PROJECTION=30 +WITH=31 +USING=32 +VIA=33 +MATERIALIZE=34 +IF=35 +ABSENT=36 +ON=37 +POLICY=38 +DEFAULT=39 +SOURCE=40 +REPOSITORY=41 +COMMIT=42 +REVISION=43 +SEMANTIC_MAJOR=44 +ON_DELETE=45 +RETAIN_OTHER=46 +KEYED=47 +PUBLIC_TRAVERSAL=48 +ID=49 +DOC=50 +MODE=51 +EMITS=52 +RECEIVER=53 +REQUIRES=54 +ANY=55 +GET=56 +SET=57 +WATCH=58 +START=59 +STOP=60 +READ=61 +WRITE=62 +RESOLVE=63 +CONNECT=64 +DISCONNECT=65 +CALL=66 +WATCH_START=67 +WATCH_STOP=68 +SUBSCRIBE=69 +UNSUBSCRIBE=70 +OPTIMISTIC_REGISTER=71 +CRDT=72 +OPTIONAL_ONE=73 +EXACTLY_ONE=74 +MANY_UNIQUE=75 +MANY=76 +ORDERED=77 +UNIT=78 +WATCH_HANDLE=79 +MESSAGE=80 +ATOM_REF=81 +INTERFACE_REF=82 +OPTIONAL=83 +LIST=84 +RECORD=85 +BOOL=86 +BYTES=87 +DOUBLE=88 +INT32=89 +INT64=90 +STRING=91 +UINT32=92 +UINT64=93 +TRUE=94 +FALSE=95 +NULL=96 +ARROW=97 +COLON=98 +SEMI=99 +COMMA=100 +DOT=101 +LBRACE=102 +RBRACE=103 +LBRACK=104 +RBRACK=105 +LPAREN=106 +RPAREN=107 +LT=108 +GT=109 +AMP=110 +EQUAL=111 +INTEGER=112 +JSON_NUMBER=113 +IDENTIFIER=114 +STRING_LITERAL=115 +LINE_COMMENT=116 +BLOCK_COMMENT=117 +WS=118 'workspace'=1 'type'=2 'object'=3 @@ -138,90 +139,91 @@ WS=117 'conform'=21 'as'=22 'bind'=23 -'to'=24 -'private'=25 -'shared'=26 -'state'=27 -'edge'=28 -'projection'=29 -'with'=30 -'using'=31 -'via'=32 -'materialize'=33 -'if'=34 -'absent'=35 -'on'=36 -'policy'=37 -'default'=38 -'source'=39 -'repository'=40 -'commit'=41 -'revision'=42 -'semantic-major'=43 -'on-delete'=44 -'retain-other'=45 -'keyed'=46 -'public-traversal'=47 -'id'=48 -'doc'=49 -'mode'=50 -'emits'=51 -'receiver'=52 -'requires'=53 -'any'=54 -'get'=55 -'set'=56 -'watch'=57 -'start'=58 -'stop'=59 -'read'=60 -'write'=61 -'resolve'=62 -'connect'=63 -'disconnect'=64 -'call'=65 -'watch-start'=66 -'watch-stop'=67 -'subscribe'=68 -'unsubscribe'=69 -'optimistic-register'=70 -'crdt'=71 -'optional-one'=72 -'exactly-one'=73 -'many-unique'=74 -'many'=75 -'ordered'=76 -'unit'=77 -'watch-handle'=78 -'message'=79 -'atom-ref'=80 -'interface-ref'=81 -'optional'=82 -'list'=83 -'record'=84 -'bool'=85 -'bytes'=86 -'double'=87 -'int32'=88 -'int64'=89 -'string'=90 -'uint32'=91 -'uint64'=92 -'true'=93 -'false'=94 -'null'=95 -'->'=96 -':'=97 -';'=98 -','=99 -'.'=100 -'{'=101 -'}'=102 -'['=103 -']'=104 -'('=105 -')'=106 -'<'=107 -'>'=108 -'&'=109 -'='=110 +'static'=24 +'to'=25 +'private'=26 +'shared'=27 +'state'=28 +'edge'=29 +'projection'=30 +'with'=31 +'using'=32 +'via'=33 +'materialize'=34 +'if'=35 +'absent'=36 +'on'=37 +'policy'=38 +'default'=39 +'source'=40 +'repository'=41 +'commit'=42 +'revision'=43 +'semantic-major'=44 +'on-delete'=45 +'retain-other'=46 +'keyed'=47 +'public-traversal'=48 +'id'=49 +'doc'=50 +'mode'=51 +'emits'=52 +'receiver'=53 +'requires'=54 +'any'=55 +'get'=56 +'set'=57 +'watch'=58 +'start'=59 +'stop'=60 +'read'=61 +'write'=62 +'resolve'=63 +'connect'=64 +'disconnect'=65 +'call'=66 +'watch-start'=67 +'watch-stop'=68 +'subscribe'=69 +'unsubscribe'=70 +'optimistic-register'=71 +'crdt'=72 +'optional-one'=73 +'exactly-one'=74 +'many-unique'=75 +'many'=76 +'ordered'=77 +'unit'=78 +'watch-handle'=79 +'message'=80 +'atom-ref'=81 +'interface-ref'=82 +'optional'=83 +'list'=84 +'record'=85 +'bool'=86 +'bytes'=87 +'double'=88 +'int32'=89 +'int64'=90 +'string'=91 +'uint32'=92 +'uint64'=93 +'true'=94 +'false'=95 +'null'=96 +'->'=97 +':'=98 +';'=99 +','=100 +'.'=101 +'{'=102 +'}'=103 +'['=104 +']'=105 +'('=106 +')'=107 +'<'=108 +'>'=109 +'&'=110 +'='=111 diff --git a/src/capability-language/generated/QuixosCapabilityLexer.ts b/src/capability-language/generated/QuixosCapabilityLexer.ts index 0aa0f03..c3baf4c 100644 --- a/src/capability-language/generated/QuixosCapabilityLexer.ts +++ b/src/capability-language/generated/QuixosCapabilityLexer.ts @@ -27,100 +27,101 @@ export class QuixosCapabilityLexer extends antlr.Lexer { public static readonly CONFORM = 21; public static readonly AS = 22; public static readonly BIND = 23; - public static readonly TO = 24; - public static readonly PRIVATE = 25; - public static readonly SHARED = 26; - public static readonly STATE = 27; - public static readonly EDGE = 28; - public static readonly PROJECTION = 29; - public static readonly WITH = 30; - public static readonly USING = 31; - public static readonly VIA = 32; - public static readonly MATERIALIZE = 33; - public static readonly IF = 34; - public static readonly ABSENT = 35; - public static readonly ON = 36; - public static readonly POLICY = 37; - public static readonly DEFAULT = 38; - public static readonly SOURCE = 39; - public static readonly REPOSITORY = 40; - public static readonly COMMIT = 41; - public static readonly REVISION = 42; - public static readonly SEMANTIC_MAJOR = 43; - public static readonly ON_DELETE = 44; - public static readonly RETAIN_OTHER = 45; - public static readonly KEYED = 46; - public static readonly PUBLIC_TRAVERSAL = 47; - public static readonly ID = 48; - public static readonly DOC = 49; - public static readonly MODE = 50; - public static readonly EMITS = 51; - public static readonly RECEIVER = 52; - public static readonly REQUIRES = 53; - public static readonly ANY = 54; - public static readonly GET = 55; - public static readonly SET = 56; - public static readonly WATCH = 57; - public static readonly START = 58; - public static readonly STOP = 59; - public static readonly READ = 60; - public static readonly WRITE = 61; - public static readonly RESOLVE = 62; - public static readonly CONNECT = 63; - public static readonly DISCONNECT = 64; - public static readonly CALL = 65; - public static readonly WATCH_START = 66; - public static readonly WATCH_STOP = 67; - public static readonly SUBSCRIBE = 68; - public static readonly UNSUBSCRIBE = 69; - public static readonly OPTIMISTIC_REGISTER = 70; - public static readonly CRDT = 71; - public static readonly OPTIONAL_ONE = 72; - public static readonly EXACTLY_ONE = 73; - public static readonly MANY_UNIQUE = 74; - public static readonly MANY = 75; - public static readonly ORDERED = 76; - public static readonly UNIT = 77; - public static readonly WATCH_HANDLE = 78; - public static readonly MESSAGE = 79; - public static readonly ATOM_REF = 80; - public static readonly INTERFACE_REF = 81; - public static readonly OPTIONAL = 82; - public static readonly LIST = 83; - public static readonly RECORD = 84; - public static readonly BOOL = 85; - public static readonly BYTES = 86; - public static readonly DOUBLE = 87; - public static readonly INT32 = 88; - public static readonly INT64 = 89; - public static readonly STRING = 90; - public static readonly UINT32 = 91; - public static readonly UINT64 = 92; - public static readonly TRUE = 93; - public static readonly FALSE = 94; - public static readonly NULL = 95; - public static readonly ARROW = 96; - public static readonly COLON = 97; - public static readonly SEMI = 98; - public static readonly COMMA = 99; - public static readonly DOT = 100; - public static readonly LBRACE = 101; - public static readonly RBRACE = 102; - public static readonly LBRACK = 103; - public static readonly RBRACK = 104; - public static readonly LPAREN = 105; - public static readonly RPAREN = 106; - public static readonly LT = 107; - public static readonly GT = 108; - public static readonly AMP = 109; - public static readonly EQUAL = 110; - public static readonly INTEGER = 111; - public static readonly JSON_NUMBER = 112; - public static readonly IDENTIFIER = 113; - public static readonly STRING_LITERAL = 114; - public static readonly LINE_COMMENT = 115; - public static readonly BLOCK_COMMENT = 116; - public static readonly WS = 117; + public static readonly STATIC = 24; + public static readonly TO = 25; + public static readonly PRIVATE = 26; + public static readonly SHARED = 27; + public static readonly STATE = 28; + public static readonly EDGE = 29; + public static readonly PROJECTION = 30; + public static readonly WITH = 31; + public static readonly USING = 32; + public static readonly VIA = 33; + public static readonly MATERIALIZE = 34; + public static readonly IF = 35; + public static readonly ABSENT = 36; + public static readonly ON = 37; + public static readonly POLICY = 38; + public static readonly DEFAULT = 39; + public static readonly SOURCE = 40; + public static readonly REPOSITORY = 41; + public static readonly COMMIT = 42; + public static readonly REVISION = 43; + public static readonly SEMANTIC_MAJOR = 44; + public static readonly ON_DELETE = 45; + public static readonly RETAIN_OTHER = 46; + public static readonly KEYED = 47; + public static readonly PUBLIC_TRAVERSAL = 48; + public static readonly ID = 49; + public static readonly DOC = 50; + public static readonly MODE = 51; + public static readonly EMITS = 52; + public static readonly RECEIVER = 53; + public static readonly REQUIRES = 54; + public static readonly ANY = 55; + public static readonly GET = 56; + public static readonly SET = 57; + public static readonly WATCH = 58; + public static readonly START = 59; + public static readonly STOP = 60; + public static readonly READ = 61; + public static readonly WRITE = 62; + public static readonly RESOLVE = 63; + public static readonly CONNECT = 64; + public static readonly DISCONNECT = 65; + public static readonly CALL = 66; + public static readonly WATCH_START = 67; + public static readonly WATCH_STOP = 68; + public static readonly SUBSCRIBE = 69; + public static readonly UNSUBSCRIBE = 70; + public static readonly OPTIMISTIC_REGISTER = 71; + public static readonly CRDT = 72; + public static readonly OPTIONAL_ONE = 73; + public static readonly EXACTLY_ONE = 74; + public static readonly MANY_UNIQUE = 75; + public static readonly MANY = 76; + public static readonly ORDERED = 77; + public static readonly UNIT = 78; + public static readonly WATCH_HANDLE = 79; + public static readonly MESSAGE = 80; + public static readonly ATOM_REF = 81; + public static readonly INTERFACE_REF = 82; + public static readonly OPTIONAL = 83; + public static readonly LIST = 84; + public static readonly RECORD = 85; + public static readonly BOOL = 86; + public static readonly BYTES = 87; + public static readonly DOUBLE = 88; + public static readonly INT32 = 89; + public static readonly INT64 = 90; + public static readonly STRING = 91; + public static readonly UINT32 = 92; + public static readonly UINT64 = 93; + public static readonly TRUE = 94; + public static readonly FALSE = 95; + public static readonly NULL = 96; + public static readonly ARROW = 97; + public static readonly COLON = 98; + public static readonly SEMI = 99; + public static readonly COMMA = 100; + public static readonly DOT = 101; + public static readonly LBRACE = 102; + public static readonly RBRACE = 103; + public static readonly LBRACK = 104; + public static readonly RBRACK = 105; + public static readonly LPAREN = 106; + public static readonly RPAREN = 107; + public static readonly LT = 108; + public static readonly GT = 109; + public static readonly AMP = 110; + public static readonly EQUAL = 111; + public static readonly INTEGER = 112; + public static readonly JSON_NUMBER = 113; + public static readonly IDENTIFIER = 114; + public static readonly STRING_LITERAL = 115; + public static readonly LINE_COMMENT = 116; + public static readonly BLOCK_COMMENT = 117; + public static readonly WS = 118; public static readonly channelNames = [ "DEFAULT_TOKEN_CHANNEL", "HIDDEN" @@ -131,17 +132,17 @@ export class QuixosCapabilityLexer extends antlr.Lexer { "'ref'", "'fragment'", "'import'", "'external'", "'atom'", "'interface'", "'interfaces'", "'package'", "'value'", "'relation'", "'operation'", "'function'", "'constructor'", "'constructs'", "'input'", "'conform'", - "'as'", "'bind'", "'to'", "'private'", "'shared'", "'state'", "'edge'", - "'projection'", "'with'", "'using'", "'via'", "'materialize'", "'if'", - "'absent'", "'on'", "'policy'", "'default'", "'source'", "'repository'", - "'commit'", "'revision'", "'semantic-major'", "'on-delete'", "'retain-other'", - "'keyed'", "'public-traversal'", "'id'", "'doc'", "'mode'", "'emits'", - "'receiver'", "'requires'", "'any'", "'get'", "'set'", "'watch'", - "'start'", "'stop'", "'read'", "'write'", "'resolve'", "'connect'", - "'disconnect'", "'call'", "'watch-start'", "'watch-stop'", "'subscribe'", - "'unsubscribe'", "'optimistic-register'", "'crdt'", "'optional-one'", - "'exactly-one'", "'many-unique'", "'many'", "'ordered'", "'unit'", - "'watch-handle'", "'message'", "'atom-ref'", "'interface-ref'", + "'as'", "'bind'", "'static'", "'to'", "'private'", "'shared'", "'state'", + "'edge'", "'projection'", "'with'", "'using'", "'via'", "'materialize'", + "'if'", "'absent'", "'on'", "'policy'", "'default'", "'source'", + "'repository'", "'commit'", "'revision'", "'semantic-major'", "'on-delete'", + "'retain-other'", "'keyed'", "'public-traversal'", "'id'", "'doc'", + "'mode'", "'emits'", "'receiver'", "'requires'", "'any'", "'get'", + "'set'", "'watch'", "'start'", "'stop'", "'read'", "'write'", "'resolve'", + "'connect'", "'disconnect'", "'call'", "'watch-start'", "'watch-stop'", + "'subscribe'", "'unsubscribe'", "'optimistic-register'", "'crdt'", + "'optional-one'", "'exactly-one'", "'many-unique'", "'many'", "'ordered'", + "'unit'", "'watch-handle'", "'message'", "'atom-ref'", "'interface-ref'", "'optional'", "'list'", "'record'", "'bool'", "'bytes'", "'double'", "'int32'", "'int64'", "'string'", "'uint32'", "'uint64'", "'true'", "'false'", "'null'", "'->'", "':'", "';'", "','", "'.'", "'{'", @@ -152,22 +153,22 @@ export class QuixosCapabilityLexer extends antlr.Lexer { null, "WORKSPACE", "TYPE", "OBJECT", "STORABLE", "IMPLEMENTS", "REF", "FRAGMENT", "IMPORT", "EXTERNAL", "ATOM", "INTERFACE", "INTERFACES", "PACKAGE", "VALUE", "RELATION", "OPERATION", "FUNCTION", "CONSTRUCTOR", - "CONSTRUCTS", "INPUT", "CONFORM", "AS", "BIND", "TO", "PRIVATE", - "SHARED", "STATE", "EDGE", "PROJECTION", "WITH", "USING", "VIA", - "MATERIALIZE", "IF", "ABSENT", "ON", "POLICY", "DEFAULT", "SOURCE", - "REPOSITORY", "COMMIT", "REVISION", "SEMANTIC_MAJOR", "ON_DELETE", - "RETAIN_OTHER", "KEYED", "PUBLIC_TRAVERSAL", "ID", "DOC", "MODE", - "EMITS", "RECEIVER", "REQUIRES", "ANY", "GET", "SET", "WATCH", "START", - "STOP", "READ", "WRITE", "RESOLVE", "CONNECT", "DISCONNECT", "CALL", - "WATCH_START", "WATCH_STOP", "SUBSCRIBE", "UNSUBSCRIBE", "OPTIMISTIC_REGISTER", - "CRDT", "OPTIONAL_ONE", "EXACTLY_ONE", "MANY_UNIQUE", "MANY", "ORDERED", - "UNIT", "WATCH_HANDLE", "MESSAGE", "ATOM_REF", "INTERFACE_REF", - "OPTIONAL", "LIST", "RECORD", "BOOL", "BYTES", "DOUBLE", "INT32", - "INT64", "STRING", "UINT32", "UINT64", "TRUE", "FALSE", "NULL", - "ARROW", "COLON", "SEMI", "COMMA", "DOT", "LBRACE", "RBRACE", "LBRACK", - "RBRACK", "LPAREN", "RPAREN", "LT", "GT", "AMP", "EQUAL", "INTEGER", - "JSON_NUMBER", "IDENTIFIER", "STRING_LITERAL", "LINE_COMMENT", "BLOCK_COMMENT", - "WS" + "CONSTRUCTS", "INPUT", "CONFORM", "AS", "BIND", "STATIC", "TO", + "PRIVATE", "SHARED", "STATE", "EDGE", "PROJECTION", "WITH", "USING", + "VIA", "MATERIALIZE", "IF", "ABSENT", "ON", "POLICY", "DEFAULT", + "SOURCE", "REPOSITORY", "COMMIT", "REVISION", "SEMANTIC_MAJOR", + "ON_DELETE", "RETAIN_OTHER", "KEYED", "PUBLIC_TRAVERSAL", "ID", + "DOC", "MODE", "EMITS", "RECEIVER", "REQUIRES", "ANY", "GET", "SET", + "WATCH", "START", "STOP", "READ", "WRITE", "RESOLVE", "CONNECT", + "DISCONNECT", "CALL", "WATCH_START", "WATCH_STOP", "SUBSCRIBE", + "UNSUBSCRIBE", "OPTIMISTIC_REGISTER", "CRDT", "OPTIONAL_ONE", "EXACTLY_ONE", + "MANY_UNIQUE", "MANY", "ORDERED", "UNIT", "WATCH_HANDLE", "MESSAGE", + "ATOM_REF", "INTERFACE_REF", "OPTIONAL", "LIST", "RECORD", "BOOL", + "BYTES", "DOUBLE", "INT32", "INT64", "STRING", "UINT32", "UINT64", + "TRUE", "FALSE", "NULL", "ARROW", "COLON", "SEMI", "COMMA", "DOT", + "LBRACE", "RBRACE", "LBRACK", "RBRACK", "LPAREN", "RPAREN", "LT", + "GT", "AMP", "EQUAL", "INTEGER", "JSON_NUMBER", "IDENTIFIER", "STRING_LITERAL", + "LINE_COMMENT", "BLOCK_COMMENT", "WS" ]; public static readonly modeNames = [ @@ -178,22 +179,22 @@ export class QuixosCapabilityLexer extends antlr.Lexer { "WORKSPACE", "TYPE", "OBJECT", "STORABLE", "IMPLEMENTS", "REF", "FRAGMENT", "IMPORT", "EXTERNAL", "ATOM", "INTERFACE", "INTERFACES", "PACKAGE", "VALUE", "RELATION", "OPERATION", "FUNCTION", "CONSTRUCTOR", - "CONSTRUCTS", "INPUT", "CONFORM", "AS", "BIND", "TO", "PRIVATE", - "SHARED", "STATE", "EDGE", "PROJECTION", "WITH", "USING", "VIA", - "MATERIALIZE", "IF", "ABSENT", "ON", "POLICY", "DEFAULT", "SOURCE", - "REPOSITORY", "COMMIT", "REVISION", "SEMANTIC_MAJOR", "ON_DELETE", - "RETAIN_OTHER", "KEYED", "PUBLIC_TRAVERSAL", "ID", "DOC", "MODE", - "EMITS", "RECEIVER", "REQUIRES", "ANY", "GET", "SET", "WATCH", "START", - "STOP", "READ", "WRITE", "RESOLVE", "CONNECT", "DISCONNECT", "CALL", - "WATCH_START", "WATCH_STOP", "SUBSCRIBE", "UNSUBSCRIBE", "OPTIMISTIC_REGISTER", - "CRDT", "OPTIONAL_ONE", "EXACTLY_ONE", "MANY_UNIQUE", "MANY", "ORDERED", - "UNIT", "WATCH_HANDLE", "MESSAGE", "ATOM_REF", "INTERFACE_REF", - "OPTIONAL", "LIST", "RECORD", "BOOL", "BYTES", "DOUBLE", "INT32", - "INT64", "STRING", "UINT32", "UINT64", "TRUE", "FALSE", "NULL", - "ARROW", "COLON", "SEMI", "COMMA", "DOT", "LBRACE", "RBRACE", "LBRACK", - "RBRACK", "LPAREN", "RPAREN", "LT", "GT", "AMP", "EQUAL", "INTEGER", - "JSON_NUMBER", "IDENTIFIER", "STRING_LITERAL", "ESC", "HEX", "LINE_COMMENT", - "BLOCK_COMMENT", "WS", + "CONSTRUCTS", "INPUT", "CONFORM", "AS", "BIND", "STATIC", "TO", + "PRIVATE", "SHARED", "STATE", "EDGE", "PROJECTION", "WITH", "USING", + "VIA", "MATERIALIZE", "IF", "ABSENT", "ON", "POLICY", "DEFAULT", + "SOURCE", "REPOSITORY", "COMMIT", "REVISION", "SEMANTIC_MAJOR", + "ON_DELETE", "RETAIN_OTHER", "KEYED", "PUBLIC_TRAVERSAL", "ID", + "DOC", "MODE", "EMITS", "RECEIVER", "REQUIRES", "ANY", "GET", "SET", + "WATCH", "START", "STOP", "READ", "WRITE", "RESOLVE", "CONNECT", + "DISCONNECT", "CALL", "WATCH_START", "WATCH_STOP", "SUBSCRIBE", + "UNSUBSCRIBE", "OPTIMISTIC_REGISTER", "CRDT", "OPTIONAL_ONE", "EXACTLY_ONE", + "MANY_UNIQUE", "MANY", "ORDERED", "UNIT", "WATCH_HANDLE", "MESSAGE", + "ATOM_REF", "INTERFACE_REF", "OPTIONAL", "LIST", "RECORD", "BOOL", + "BYTES", "DOUBLE", "INT32", "INT64", "STRING", "UINT32", "UINT64", + "TRUE", "FALSE", "NULL", "ARROW", "COLON", "SEMI", "COMMA", "DOT", + "LBRACE", "RBRACE", "LBRACK", "RBRACK", "LPAREN", "RPAREN", "LT", + "GT", "AMP", "EQUAL", "INTEGER", "JSON_NUMBER", "IDENTIFIER", "STRING_LITERAL", + "ESC", "HEX", "LINE_COMMENT", "BLOCK_COMMENT", "WS", ]; @@ -215,7 +216,7 @@ export class QuixosCapabilityLexer extends antlr.Lexer { public get modeNames(): string[] { return QuixosCapabilityLexer.modeNames; } public static readonly _serializedATN: number[] = [ - 4,0,117,1110,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7, + 4,0,118,1119,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7, 5,2,6,7,6,2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12, 2,13,7,13,2,14,7,14,2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19, 7,19,2,20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25, @@ -233,389 +234,392 @@ export class QuixosCapabilityLexer extends antlr.Lexer { 7,97,2,98,7,98,2,99,7,99,2,100,7,100,2,101,7,101,2,102,7,102,2,103, 7,103,2,104,7,104,2,105,7,105,2,106,7,106,2,107,7,107,2,108,7,108, 2,109,7,109,2,110,7,110,2,111,7,111,2,112,7,112,2,113,7,113,2,114, - 7,114,2,115,7,115,2,116,7,116,2,117,7,117,2,118,7,118,1,0,1,0,1, - 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,2,1,2,1,2,1, - 2,1,2,1,2,1,2,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,4,1,4,1,4,1, - 4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,5,1,5,1,5,1,5,1,6,1,6,1,6,1,6,1, - 6,1,6,1,6,1,6,1,6,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,8,1,8,1,8,1,8,1, - 8,1,8,1,8,1,8,1,8,1,9,1,9,1,9,1,9,1,9,1,10,1,10,1,10,1,10,1,10,1, - 10,1,10,1,10,1,10,1,10,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1, - 11,1,11,1,11,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,13,1,13,1, - 13,1,13,1,13,1,13,1,14,1,14,1,14,1,14,1,14,1,14,1,14,1,14,1,14,1, - 15,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,16,1,16,1,16,1, - 16,1,16,1,16,1,16,1,16,1,16,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1, - 17,1,17,1,17,1,17,1,17,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1, - 18,1,18,1,18,1,19,1,19,1,19,1,19,1,19,1,19,1,20,1,20,1,20,1,20,1, - 20,1,20,1,20,1,20,1,21,1,21,1,21,1,22,1,22,1,22,1,22,1,22,1,23,1, - 23,1,23,1,24,1,24,1,24,1,24,1,24,1,24,1,24,1,24,1,25,1,25,1,25,1, - 25,1,25,1,25,1,25,1,26,1,26,1,26,1,26,1,26,1,26,1,27,1,27,1,27,1, - 27,1,27,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1, - 29,1,29,1,29,1,29,1,29,1,30,1,30,1,30,1,30,1,30,1,30,1,31,1,31,1, - 31,1,31,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1, - 32,1,33,1,33,1,33,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,35,1,35,1, - 35,1,36,1,36,1,36,1,36,1,36,1,36,1,36,1,37,1,37,1,37,1,37,1,37,1, - 37,1,37,1,37,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,39,1,39,1,39,1, - 39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,40,1,40,1,40,1,40,1,40,1, - 40,1,40,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,42,1,42,1, - 42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1, - 43,1,43,1,43,1,43,1,43,1,43,1,43,1,43,1,43,1,43,1,44,1,44,1,44,1, - 44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,45,1,45,1,45,1, - 45,1,45,1,45,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1, - 46,1,46,1,46,1,46,1,46,1,46,1,46,1,47,1,47,1,47,1,48,1,48,1,48,1, - 48,1,49,1,49,1,49,1,49,1,49,1,50,1,50,1,50,1,50,1,50,1,50,1,51,1, - 51,1,51,1,51,1,51,1,51,1,51,1,51,1,51,1,52,1,52,1,52,1,52,1,52,1, - 52,1,52,1,52,1,52,1,53,1,53,1,53,1,53,1,54,1,54,1,54,1,54,1,55,1, - 55,1,55,1,55,1,56,1,56,1,56,1,56,1,56,1,56,1,57,1,57,1,57,1,57,1, - 57,1,57,1,58,1,58,1,58,1,58,1,58,1,59,1,59,1,59,1,59,1,59,1,60,1, - 60,1,60,1,60,1,60,1,60,1,61,1,61,1,61,1,61,1,61,1,61,1,61,1,61,1, - 62,1,62,1,62,1,62,1,62,1,62,1,62,1,62,1,63,1,63,1,63,1,63,1,63,1, - 63,1,63,1,63,1,63,1,63,1,63,1,64,1,64,1,64,1,64,1,64,1,65,1,65,1, - 65,1,65,1,65,1,65,1,65,1,65,1,65,1,65,1,65,1,65,1,66,1,66,1,66,1, - 66,1,66,1,66,1,66,1,66,1,66,1,66,1,66,1,67,1,67,1,67,1,67,1,67,1, - 67,1,67,1,67,1,67,1,67,1,68,1,68,1,68,1,68,1,68,1,68,1,68,1,68,1, - 68,1,68,1,68,1,68,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1, - 69,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,70,1,70,1, - 70,1,70,1,70,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1, - 71,1,71,1,71,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1, - 72,1,72,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1, - 73,1,74,1,74,1,74,1,74,1,74,1,75,1,75,1,75,1,75,1,75,1,75,1,75,1, - 75,1,76,1,76,1,76,1,76,1,76,1,77,1,77,1,77,1,77,1,77,1,77,1,77,1, - 77,1,77,1,77,1,77,1,77,1,77,1,78,1,78,1,78,1,78,1,78,1,78,1,78,1, - 78,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,80,1,80,1,80,1, - 80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,80,1,81,1,81,1, - 81,1,81,1,81,1,81,1,81,1,81,1,81,1,82,1,82,1,82,1,82,1,82,1,83,1, - 83,1,83,1,83,1,83,1,83,1,83,1,84,1,84,1,84,1,84,1,84,1,85,1,85,1, - 85,1,85,1,85,1,85,1,86,1,86,1,86,1,86,1,86,1,86,1,86,1,87,1,87,1, - 87,1,87,1,87,1,87,1,88,1,88,1,88,1,88,1,88,1,88,1,89,1,89,1,89,1, - 89,1,89,1,89,1,89,1,90,1,90,1,90,1,90,1,90,1,90,1,90,1,91,1,91,1, - 91,1,91,1,91,1,91,1,91,1,92,1,92,1,92,1,92,1,92,1,93,1,93,1,93,1, - 93,1,93,1,93,1,94,1,94,1,94,1,94,1,94,1,95,1,95,1,95,1,96,1,96,1, - 97,1,97,1,98,1,98,1,99,1,99,1,100,1,100,1,101,1,101,1,102,1,102, - 1,103,1,103,1,104,1,104,1,105,1,105,1,106,1,106,1,107,1,107,1,108, - 1,108,1,109,1,109,1,110,3,110,1011,8,110,1,110,4,110,1014,8,110, - 11,110,12,110,1015,1,111,3,111,1019,8,111,1,111,1,111,1,111,5,111, - 1024,8,111,10,111,12,111,1027,9,111,3,111,1029,8,111,1,111,1,111, - 4,111,1033,8,111,11,111,12,111,1034,3,111,1037,8,111,1,111,1,111, - 3,111,1041,8,111,1,111,4,111,1044,8,111,11,111,12,111,1045,3,111, - 1048,8,111,1,112,1,112,5,112,1052,8,112,10,112,12,112,1055,9,112, - 1,113,1,113,1,113,5,113,1060,8,113,10,113,12,113,1063,9,113,1,113, - 1,113,1,114,1,114,1,114,1,114,1,114,1,114,1,114,1,114,3,114,1075, - 8,114,1,115,1,115,1,116,1,116,1,116,1,116,5,116,1083,8,116,10,116, - 12,116,1086,9,116,1,116,1,116,1,117,1,117,1,117,1,117,5,117,1094, - 8,117,10,117,12,117,1097,9,117,1,117,1,117,1,117,1,117,1,117,1,118, - 4,118,1105,8,118,11,118,12,118,1106,1,118,1,118,1,1095,0,119,1,1, - 3,2,5,3,7,4,9,5,11,6,13,7,15,8,17,9,19,10,21,11,23,12,25,13,27,14, - 29,15,31,16,33,17,35,18,37,19,39,20,41,21,43,22,45,23,47,24,49,25, - 51,26,53,27,55,28,57,29,59,30,61,31,63,32,65,33,67,34,69,35,71,36, - 73,37,75,38,77,39,79,40,81,41,83,42,85,43,87,44,89,45,91,46,93,47, - 95,48,97,49,99,50,101,51,103,52,105,53,107,54,109,55,111,56,113, - 57,115,58,117,59,119,60,121,61,123,62,125,63,127,64,129,65,131,66, - 133,67,135,68,137,69,139,70,141,71,143,72,145,73,147,74,149,75,151, - 76,153,77,155,78,157,79,159,80,161,81,163,82,165,83,167,84,169,85, - 171,86,173,87,175,88,177,89,179,90,181,91,183,92,185,93,187,94,189, - 95,191,96,193,97,195,98,197,99,199,100,201,101,203,102,205,103,207, - 104,209,105,211,106,213,107,215,108,217,109,219,110,221,111,223, - 112,225,113,227,114,229,0,231,0,233,115,235,116,237,117,1,0,11,1, - 0,48,57,1,0,49,57,2,0,69,69,101,101,2,0,43,43,45,45,3,0,65,90,95, - 95,97,122,4,0,48,57,65,90,95,95,97,122,4,0,10,10,13,13,34,34,92, - 92,8,0,34,34,47,47,92,92,98,98,102,102,110,110,114,114,116,116,3, - 0,48,57,65,70,97,102,2,0,10,10,13,13,3,0,9,10,13,13,32,32,1124,0, - 1,1,0,0,0,0,3,1,0,0,0,0,5,1,0,0,0,0,7,1,0,0,0,0,9,1,0,0,0,0,11,1, - 0,0,0,0,13,1,0,0,0,0,15,1,0,0,0,0,17,1,0,0,0,0,19,1,0,0,0,0,21,1, - 0,0,0,0,23,1,0,0,0,0,25,1,0,0,0,0,27,1,0,0,0,0,29,1,0,0,0,0,31,1, - 0,0,0,0,33,1,0,0,0,0,35,1,0,0,0,0,37,1,0,0,0,0,39,1,0,0,0,0,41,1, - 0,0,0,0,43,1,0,0,0,0,45,1,0,0,0,0,47,1,0,0,0,0,49,1,0,0,0,0,51,1, - 0,0,0,0,53,1,0,0,0,0,55,1,0,0,0,0,57,1,0,0,0,0,59,1,0,0,0,0,61,1, - 0,0,0,0,63,1,0,0,0,0,65,1,0,0,0,0,67,1,0,0,0,0,69,1,0,0,0,0,71,1, - 0,0,0,0,73,1,0,0,0,0,75,1,0,0,0,0,77,1,0,0,0,0,79,1,0,0,0,0,81,1, - 0,0,0,0,83,1,0,0,0,0,85,1,0,0,0,0,87,1,0,0,0,0,89,1,0,0,0,0,91,1, - 0,0,0,0,93,1,0,0,0,0,95,1,0,0,0,0,97,1,0,0,0,0,99,1,0,0,0,0,101, - 1,0,0,0,0,103,1,0,0,0,0,105,1,0,0,0,0,107,1,0,0,0,0,109,1,0,0,0, - 0,111,1,0,0,0,0,113,1,0,0,0,0,115,1,0,0,0,0,117,1,0,0,0,0,119,1, - 0,0,0,0,121,1,0,0,0,0,123,1,0,0,0,0,125,1,0,0,0,0,127,1,0,0,0,0, - 129,1,0,0,0,0,131,1,0,0,0,0,133,1,0,0,0,0,135,1,0,0,0,0,137,1,0, - 0,0,0,139,1,0,0,0,0,141,1,0,0,0,0,143,1,0,0,0,0,145,1,0,0,0,0,147, - 1,0,0,0,0,149,1,0,0,0,0,151,1,0,0,0,0,153,1,0,0,0,0,155,1,0,0,0, - 0,157,1,0,0,0,0,159,1,0,0,0,0,161,1,0,0,0,0,163,1,0,0,0,0,165,1, - 0,0,0,0,167,1,0,0,0,0,169,1,0,0,0,0,171,1,0,0,0,0,173,1,0,0,0,0, - 175,1,0,0,0,0,177,1,0,0,0,0,179,1,0,0,0,0,181,1,0,0,0,0,183,1,0, - 0,0,0,185,1,0,0,0,0,187,1,0,0,0,0,189,1,0,0,0,0,191,1,0,0,0,0,193, - 1,0,0,0,0,195,1,0,0,0,0,197,1,0,0,0,0,199,1,0,0,0,0,201,1,0,0,0, - 0,203,1,0,0,0,0,205,1,0,0,0,0,207,1,0,0,0,0,209,1,0,0,0,0,211,1, - 0,0,0,0,213,1,0,0,0,0,215,1,0,0,0,0,217,1,0,0,0,0,219,1,0,0,0,0, - 221,1,0,0,0,0,223,1,0,0,0,0,225,1,0,0,0,0,227,1,0,0,0,0,233,1,0, - 0,0,0,235,1,0,0,0,0,237,1,0,0,0,1,239,1,0,0,0,3,249,1,0,0,0,5,254, - 1,0,0,0,7,261,1,0,0,0,9,270,1,0,0,0,11,281,1,0,0,0,13,285,1,0,0, - 0,15,294,1,0,0,0,17,301,1,0,0,0,19,310,1,0,0,0,21,315,1,0,0,0,23, - 325,1,0,0,0,25,336,1,0,0,0,27,344,1,0,0,0,29,350,1,0,0,0,31,359, - 1,0,0,0,33,369,1,0,0,0,35,378,1,0,0,0,37,390,1,0,0,0,39,401,1,0, - 0,0,41,407,1,0,0,0,43,415,1,0,0,0,45,418,1,0,0,0,47,423,1,0,0,0, - 49,426,1,0,0,0,51,434,1,0,0,0,53,441,1,0,0,0,55,447,1,0,0,0,57,452, - 1,0,0,0,59,463,1,0,0,0,61,468,1,0,0,0,63,474,1,0,0,0,65,478,1,0, - 0,0,67,490,1,0,0,0,69,493,1,0,0,0,71,500,1,0,0,0,73,503,1,0,0,0, - 75,510,1,0,0,0,77,518,1,0,0,0,79,525,1,0,0,0,81,536,1,0,0,0,83,543, - 1,0,0,0,85,552,1,0,0,0,87,567,1,0,0,0,89,577,1,0,0,0,91,590,1,0, - 0,0,93,596,1,0,0,0,95,613,1,0,0,0,97,616,1,0,0,0,99,620,1,0,0,0, - 101,625,1,0,0,0,103,631,1,0,0,0,105,640,1,0,0,0,107,649,1,0,0,0, - 109,653,1,0,0,0,111,657,1,0,0,0,113,661,1,0,0,0,115,667,1,0,0,0, - 117,673,1,0,0,0,119,678,1,0,0,0,121,683,1,0,0,0,123,689,1,0,0,0, - 125,697,1,0,0,0,127,705,1,0,0,0,129,716,1,0,0,0,131,721,1,0,0,0, - 133,733,1,0,0,0,135,744,1,0,0,0,137,754,1,0,0,0,139,766,1,0,0,0, - 141,786,1,0,0,0,143,791,1,0,0,0,145,804,1,0,0,0,147,816,1,0,0,0, - 149,828,1,0,0,0,151,833,1,0,0,0,153,841,1,0,0,0,155,846,1,0,0,0, - 157,859,1,0,0,0,159,867,1,0,0,0,161,876,1,0,0,0,163,890,1,0,0,0, - 165,899,1,0,0,0,167,904,1,0,0,0,169,911,1,0,0,0,171,916,1,0,0,0, - 173,922,1,0,0,0,175,929,1,0,0,0,177,935,1,0,0,0,179,941,1,0,0,0, - 181,948,1,0,0,0,183,955,1,0,0,0,185,962,1,0,0,0,187,967,1,0,0,0, - 189,973,1,0,0,0,191,978,1,0,0,0,193,981,1,0,0,0,195,983,1,0,0,0, - 197,985,1,0,0,0,199,987,1,0,0,0,201,989,1,0,0,0,203,991,1,0,0,0, - 205,993,1,0,0,0,207,995,1,0,0,0,209,997,1,0,0,0,211,999,1,0,0,0, - 213,1001,1,0,0,0,215,1003,1,0,0,0,217,1005,1,0,0,0,219,1007,1,0, - 0,0,221,1010,1,0,0,0,223,1018,1,0,0,0,225,1049,1,0,0,0,227,1056, - 1,0,0,0,229,1066,1,0,0,0,231,1076,1,0,0,0,233,1078,1,0,0,0,235,1089, - 1,0,0,0,237,1104,1,0,0,0,239,240,5,119,0,0,240,241,5,111,0,0,241, - 242,5,114,0,0,242,243,5,107,0,0,243,244,5,115,0,0,244,245,5,112, - 0,0,245,246,5,97,0,0,246,247,5,99,0,0,247,248,5,101,0,0,248,2,1, - 0,0,0,249,250,5,116,0,0,250,251,5,121,0,0,251,252,5,112,0,0,252, - 253,5,101,0,0,253,4,1,0,0,0,254,255,5,111,0,0,255,256,5,98,0,0,256, - 257,5,106,0,0,257,258,5,101,0,0,258,259,5,99,0,0,259,260,5,116,0, - 0,260,6,1,0,0,0,261,262,5,115,0,0,262,263,5,116,0,0,263,264,5,111, - 0,0,264,265,5,114,0,0,265,266,5,97,0,0,266,267,5,98,0,0,267,268, - 5,108,0,0,268,269,5,101,0,0,269,8,1,0,0,0,270,271,5,105,0,0,271, - 272,5,109,0,0,272,273,5,112,0,0,273,274,5,108,0,0,274,275,5,101, - 0,0,275,276,5,109,0,0,276,277,5,101,0,0,277,278,5,110,0,0,278,279, - 5,116,0,0,279,280,5,115,0,0,280,10,1,0,0,0,281,282,5,114,0,0,282, - 283,5,101,0,0,283,284,5,102,0,0,284,12,1,0,0,0,285,286,5,102,0,0, - 286,287,5,114,0,0,287,288,5,97,0,0,288,289,5,103,0,0,289,290,5,109, - 0,0,290,291,5,101,0,0,291,292,5,110,0,0,292,293,5,116,0,0,293,14, - 1,0,0,0,294,295,5,105,0,0,295,296,5,109,0,0,296,297,5,112,0,0,297, - 298,5,111,0,0,298,299,5,114,0,0,299,300,5,116,0,0,300,16,1,0,0,0, - 301,302,5,101,0,0,302,303,5,120,0,0,303,304,5,116,0,0,304,305,5, - 101,0,0,305,306,5,114,0,0,306,307,5,110,0,0,307,308,5,97,0,0,308, - 309,5,108,0,0,309,18,1,0,0,0,310,311,5,97,0,0,311,312,5,116,0,0, - 312,313,5,111,0,0,313,314,5,109,0,0,314,20,1,0,0,0,315,316,5,105, - 0,0,316,317,5,110,0,0,317,318,5,116,0,0,318,319,5,101,0,0,319,320, - 5,114,0,0,320,321,5,102,0,0,321,322,5,97,0,0,322,323,5,99,0,0,323, - 324,5,101,0,0,324,22,1,0,0,0,325,326,5,105,0,0,326,327,5,110,0,0, - 327,328,5,116,0,0,328,329,5,101,0,0,329,330,5,114,0,0,330,331,5, - 102,0,0,331,332,5,97,0,0,332,333,5,99,0,0,333,334,5,101,0,0,334, - 335,5,115,0,0,335,24,1,0,0,0,336,337,5,112,0,0,337,338,5,97,0,0, - 338,339,5,99,0,0,339,340,5,107,0,0,340,341,5,97,0,0,341,342,5,103, - 0,0,342,343,5,101,0,0,343,26,1,0,0,0,344,345,5,118,0,0,345,346,5, - 97,0,0,346,347,5,108,0,0,347,348,5,117,0,0,348,349,5,101,0,0,349, - 28,1,0,0,0,350,351,5,114,0,0,351,352,5,101,0,0,352,353,5,108,0,0, - 353,354,5,97,0,0,354,355,5,116,0,0,355,356,5,105,0,0,356,357,5,111, - 0,0,357,358,5,110,0,0,358,30,1,0,0,0,359,360,5,111,0,0,360,361,5, - 112,0,0,361,362,5,101,0,0,362,363,5,114,0,0,363,364,5,97,0,0,364, - 365,5,116,0,0,365,366,5,105,0,0,366,367,5,111,0,0,367,368,5,110, - 0,0,368,32,1,0,0,0,369,370,5,102,0,0,370,371,5,117,0,0,371,372,5, - 110,0,0,372,373,5,99,0,0,373,374,5,116,0,0,374,375,5,105,0,0,375, - 376,5,111,0,0,376,377,5,110,0,0,377,34,1,0,0,0,378,379,5,99,0,0, - 379,380,5,111,0,0,380,381,5,110,0,0,381,382,5,115,0,0,382,383,5, - 116,0,0,383,384,5,114,0,0,384,385,5,117,0,0,385,386,5,99,0,0,386, - 387,5,116,0,0,387,388,5,111,0,0,388,389,5,114,0,0,389,36,1,0,0,0, - 390,391,5,99,0,0,391,392,5,111,0,0,392,393,5,110,0,0,393,394,5,115, - 0,0,394,395,5,116,0,0,395,396,5,114,0,0,396,397,5,117,0,0,397,398, - 5,99,0,0,398,399,5,116,0,0,399,400,5,115,0,0,400,38,1,0,0,0,401, - 402,5,105,0,0,402,403,5,110,0,0,403,404,5,112,0,0,404,405,5,117, - 0,0,405,406,5,116,0,0,406,40,1,0,0,0,407,408,5,99,0,0,408,409,5, - 111,0,0,409,410,5,110,0,0,410,411,5,102,0,0,411,412,5,111,0,0,412, - 413,5,114,0,0,413,414,5,109,0,0,414,42,1,0,0,0,415,416,5,97,0,0, - 416,417,5,115,0,0,417,44,1,0,0,0,418,419,5,98,0,0,419,420,5,105, - 0,0,420,421,5,110,0,0,421,422,5,100,0,0,422,46,1,0,0,0,423,424,5, - 116,0,0,424,425,5,111,0,0,425,48,1,0,0,0,426,427,5,112,0,0,427,428, - 5,114,0,0,428,429,5,105,0,0,429,430,5,118,0,0,430,431,5,97,0,0,431, - 432,5,116,0,0,432,433,5,101,0,0,433,50,1,0,0,0,434,435,5,115,0,0, - 435,436,5,104,0,0,436,437,5,97,0,0,437,438,5,114,0,0,438,439,5,101, - 0,0,439,440,5,100,0,0,440,52,1,0,0,0,441,442,5,115,0,0,442,443,5, - 116,0,0,443,444,5,97,0,0,444,445,5,116,0,0,445,446,5,101,0,0,446, - 54,1,0,0,0,447,448,5,101,0,0,448,449,5,100,0,0,449,450,5,103,0,0, - 450,451,5,101,0,0,451,56,1,0,0,0,452,453,5,112,0,0,453,454,5,114, - 0,0,454,455,5,111,0,0,455,456,5,106,0,0,456,457,5,101,0,0,457,458, - 5,99,0,0,458,459,5,116,0,0,459,460,5,105,0,0,460,461,5,111,0,0,461, - 462,5,110,0,0,462,58,1,0,0,0,463,464,5,119,0,0,464,465,5,105,0,0, - 465,466,5,116,0,0,466,467,5,104,0,0,467,60,1,0,0,0,468,469,5,117, - 0,0,469,470,5,115,0,0,470,471,5,105,0,0,471,472,5,110,0,0,472,473, - 5,103,0,0,473,62,1,0,0,0,474,475,5,118,0,0,475,476,5,105,0,0,476, - 477,5,97,0,0,477,64,1,0,0,0,478,479,5,109,0,0,479,480,5,97,0,0,480, - 481,5,116,0,0,481,482,5,101,0,0,482,483,5,114,0,0,483,484,5,105, - 0,0,484,485,5,97,0,0,485,486,5,108,0,0,486,487,5,105,0,0,487,488, - 5,122,0,0,488,489,5,101,0,0,489,66,1,0,0,0,490,491,5,105,0,0,491, - 492,5,102,0,0,492,68,1,0,0,0,493,494,5,97,0,0,494,495,5,98,0,0,495, - 496,5,115,0,0,496,497,5,101,0,0,497,498,5,110,0,0,498,499,5,116, - 0,0,499,70,1,0,0,0,500,501,5,111,0,0,501,502,5,110,0,0,502,72,1, - 0,0,0,503,504,5,112,0,0,504,505,5,111,0,0,505,506,5,108,0,0,506, - 507,5,105,0,0,507,508,5,99,0,0,508,509,5,121,0,0,509,74,1,0,0,0, - 510,511,5,100,0,0,511,512,5,101,0,0,512,513,5,102,0,0,513,514,5, - 97,0,0,514,515,5,117,0,0,515,516,5,108,0,0,516,517,5,116,0,0,517, - 76,1,0,0,0,518,519,5,115,0,0,519,520,5,111,0,0,520,521,5,117,0,0, - 521,522,5,114,0,0,522,523,5,99,0,0,523,524,5,101,0,0,524,78,1,0, - 0,0,525,526,5,114,0,0,526,527,5,101,0,0,527,528,5,112,0,0,528,529, - 5,111,0,0,529,530,5,115,0,0,530,531,5,105,0,0,531,532,5,116,0,0, - 532,533,5,111,0,0,533,534,5,114,0,0,534,535,5,121,0,0,535,80,1,0, - 0,0,536,537,5,99,0,0,537,538,5,111,0,0,538,539,5,109,0,0,539,540, - 5,109,0,0,540,541,5,105,0,0,541,542,5,116,0,0,542,82,1,0,0,0,543, - 544,5,114,0,0,544,545,5,101,0,0,545,546,5,118,0,0,546,547,5,105, - 0,0,547,548,5,115,0,0,548,549,5,105,0,0,549,550,5,111,0,0,550,551, - 5,110,0,0,551,84,1,0,0,0,552,553,5,115,0,0,553,554,5,101,0,0,554, - 555,5,109,0,0,555,556,5,97,0,0,556,557,5,110,0,0,557,558,5,116,0, - 0,558,559,5,105,0,0,559,560,5,99,0,0,560,561,5,45,0,0,561,562,5, - 109,0,0,562,563,5,97,0,0,563,564,5,106,0,0,564,565,5,111,0,0,565, - 566,5,114,0,0,566,86,1,0,0,0,567,568,5,111,0,0,568,569,5,110,0,0, - 569,570,5,45,0,0,570,571,5,100,0,0,571,572,5,101,0,0,572,573,5,108, - 0,0,573,574,5,101,0,0,574,575,5,116,0,0,575,576,5,101,0,0,576,88, - 1,0,0,0,577,578,5,114,0,0,578,579,5,101,0,0,579,580,5,116,0,0,580, - 581,5,97,0,0,581,582,5,105,0,0,582,583,5,110,0,0,583,584,5,45,0, - 0,584,585,5,111,0,0,585,586,5,116,0,0,586,587,5,104,0,0,587,588, - 5,101,0,0,588,589,5,114,0,0,589,90,1,0,0,0,590,591,5,107,0,0,591, - 592,5,101,0,0,592,593,5,121,0,0,593,594,5,101,0,0,594,595,5,100, - 0,0,595,92,1,0,0,0,596,597,5,112,0,0,597,598,5,117,0,0,598,599,5, - 98,0,0,599,600,5,108,0,0,600,601,5,105,0,0,601,602,5,99,0,0,602, - 603,5,45,0,0,603,604,5,116,0,0,604,605,5,114,0,0,605,606,5,97,0, - 0,606,607,5,118,0,0,607,608,5,101,0,0,608,609,5,114,0,0,609,610, - 5,115,0,0,610,611,5,97,0,0,611,612,5,108,0,0,612,94,1,0,0,0,613, - 614,5,105,0,0,614,615,5,100,0,0,615,96,1,0,0,0,616,617,5,100,0,0, - 617,618,5,111,0,0,618,619,5,99,0,0,619,98,1,0,0,0,620,621,5,109, - 0,0,621,622,5,111,0,0,622,623,5,100,0,0,623,624,5,101,0,0,624,100, - 1,0,0,0,625,626,5,101,0,0,626,627,5,109,0,0,627,628,5,105,0,0,628, - 629,5,116,0,0,629,630,5,115,0,0,630,102,1,0,0,0,631,632,5,114,0, - 0,632,633,5,101,0,0,633,634,5,99,0,0,634,635,5,101,0,0,635,636,5, - 105,0,0,636,637,5,118,0,0,637,638,5,101,0,0,638,639,5,114,0,0,639, - 104,1,0,0,0,640,641,5,114,0,0,641,642,5,101,0,0,642,643,5,113,0, - 0,643,644,5,117,0,0,644,645,5,105,0,0,645,646,5,114,0,0,646,647, - 5,101,0,0,647,648,5,115,0,0,648,106,1,0,0,0,649,650,5,97,0,0,650, - 651,5,110,0,0,651,652,5,121,0,0,652,108,1,0,0,0,653,654,5,103,0, - 0,654,655,5,101,0,0,655,656,5,116,0,0,656,110,1,0,0,0,657,658,5, - 115,0,0,658,659,5,101,0,0,659,660,5,116,0,0,660,112,1,0,0,0,661, - 662,5,119,0,0,662,663,5,97,0,0,663,664,5,116,0,0,664,665,5,99,0, - 0,665,666,5,104,0,0,666,114,1,0,0,0,667,668,5,115,0,0,668,669,5, - 116,0,0,669,670,5,97,0,0,670,671,5,114,0,0,671,672,5,116,0,0,672, - 116,1,0,0,0,673,674,5,115,0,0,674,675,5,116,0,0,675,676,5,111,0, - 0,676,677,5,112,0,0,677,118,1,0,0,0,678,679,5,114,0,0,679,680,5, - 101,0,0,680,681,5,97,0,0,681,682,5,100,0,0,682,120,1,0,0,0,683,684, - 5,119,0,0,684,685,5,114,0,0,685,686,5,105,0,0,686,687,5,116,0,0, - 687,688,5,101,0,0,688,122,1,0,0,0,689,690,5,114,0,0,690,691,5,101, - 0,0,691,692,5,115,0,0,692,693,5,111,0,0,693,694,5,108,0,0,694,695, - 5,118,0,0,695,696,5,101,0,0,696,124,1,0,0,0,697,698,5,99,0,0,698, - 699,5,111,0,0,699,700,5,110,0,0,700,701,5,110,0,0,701,702,5,101, - 0,0,702,703,5,99,0,0,703,704,5,116,0,0,704,126,1,0,0,0,705,706,5, - 100,0,0,706,707,5,105,0,0,707,708,5,115,0,0,708,709,5,99,0,0,709, - 710,5,111,0,0,710,711,5,110,0,0,711,712,5,110,0,0,712,713,5,101, - 0,0,713,714,5,99,0,0,714,715,5,116,0,0,715,128,1,0,0,0,716,717,5, - 99,0,0,717,718,5,97,0,0,718,719,5,108,0,0,719,720,5,108,0,0,720, - 130,1,0,0,0,721,722,5,119,0,0,722,723,5,97,0,0,723,724,5,116,0,0, - 724,725,5,99,0,0,725,726,5,104,0,0,726,727,5,45,0,0,727,728,5,115, - 0,0,728,729,5,116,0,0,729,730,5,97,0,0,730,731,5,114,0,0,731,732, - 5,116,0,0,732,132,1,0,0,0,733,734,5,119,0,0,734,735,5,97,0,0,735, - 736,5,116,0,0,736,737,5,99,0,0,737,738,5,104,0,0,738,739,5,45,0, - 0,739,740,5,115,0,0,740,741,5,116,0,0,741,742,5,111,0,0,742,743, - 5,112,0,0,743,134,1,0,0,0,744,745,5,115,0,0,745,746,5,117,0,0,746, - 747,5,98,0,0,747,748,5,115,0,0,748,749,5,99,0,0,749,750,5,114,0, - 0,750,751,5,105,0,0,751,752,5,98,0,0,752,753,5,101,0,0,753,136,1, - 0,0,0,754,755,5,117,0,0,755,756,5,110,0,0,756,757,5,115,0,0,757, - 758,5,117,0,0,758,759,5,98,0,0,759,760,5,115,0,0,760,761,5,99,0, - 0,761,762,5,114,0,0,762,763,5,105,0,0,763,764,5,98,0,0,764,765,5, - 101,0,0,765,138,1,0,0,0,766,767,5,111,0,0,767,768,5,112,0,0,768, - 769,5,116,0,0,769,770,5,105,0,0,770,771,5,109,0,0,771,772,5,105, - 0,0,772,773,5,115,0,0,773,774,5,116,0,0,774,775,5,105,0,0,775,776, - 5,99,0,0,776,777,5,45,0,0,777,778,5,114,0,0,778,779,5,101,0,0,779, - 780,5,103,0,0,780,781,5,105,0,0,781,782,5,115,0,0,782,783,5,116, - 0,0,783,784,5,101,0,0,784,785,5,114,0,0,785,140,1,0,0,0,786,787, - 5,99,0,0,787,788,5,114,0,0,788,789,5,100,0,0,789,790,5,116,0,0,790, - 142,1,0,0,0,791,792,5,111,0,0,792,793,5,112,0,0,793,794,5,116,0, - 0,794,795,5,105,0,0,795,796,5,111,0,0,796,797,5,110,0,0,797,798, - 5,97,0,0,798,799,5,108,0,0,799,800,5,45,0,0,800,801,5,111,0,0,801, - 802,5,110,0,0,802,803,5,101,0,0,803,144,1,0,0,0,804,805,5,101,0, - 0,805,806,5,120,0,0,806,807,5,97,0,0,807,808,5,99,0,0,808,809,5, - 116,0,0,809,810,5,108,0,0,810,811,5,121,0,0,811,812,5,45,0,0,812, - 813,5,111,0,0,813,814,5,110,0,0,814,815,5,101,0,0,815,146,1,0,0, - 0,816,817,5,109,0,0,817,818,5,97,0,0,818,819,5,110,0,0,819,820,5, - 121,0,0,820,821,5,45,0,0,821,822,5,117,0,0,822,823,5,110,0,0,823, - 824,5,105,0,0,824,825,5,113,0,0,825,826,5,117,0,0,826,827,5,101, - 0,0,827,148,1,0,0,0,828,829,5,109,0,0,829,830,5,97,0,0,830,831,5, - 110,0,0,831,832,5,121,0,0,832,150,1,0,0,0,833,834,5,111,0,0,834, - 835,5,114,0,0,835,836,5,100,0,0,836,837,5,101,0,0,837,838,5,114, - 0,0,838,839,5,101,0,0,839,840,5,100,0,0,840,152,1,0,0,0,841,842, - 5,117,0,0,842,843,5,110,0,0,843,844,5,105,0,0,844,845,5,116,0,0, - 845,154,1,0,0,0,846,847,5,119,0,0,847,848,5,97,0,0,848,849,5,116, - 0,0,849,850,5,99,0,0,850,851,5,104,0,0,851,852,5,45,0,0,852,853, - 5,104,0,0,853,854,5,97,0,0,854,855,5,110,0,0,855,856,5,100,0,0,856, - 857,5,108,0,0,857,858,5,101,0,0,858,156,1,0,0,0,859,860,5,109,0, - 0,860,861,5,101,0,0,861,862,5,115,0,0,862,863,5,115,0,0,863,864, - 5,97,0,0,864,865,5,103,0,0,865,866,5,101,0,0,866,158,1,0,0,0,867, - 868,5,97,0,0,868,869,5,116,0,0,869,870,5,111,0,0,870,871,5,109,0, - 0,871,872,5,45,0,0,872,873,5,114,0,0,873,874,5,101,0,0,874,875,5, - 102,0,0,875,160,1,0,0,0,876,877,5,105,0,0,877,878,5,110,0,0,878, - 879,5,116,0,0,879,880,5,101,0,0,880,881,5,114,0,0,881,882,5,102, - 0,0,882,883,5,97,0,0,883,884,5,99,0,0,884,885,5,101,0,0,885,886, - 5,45,0,0,886,887,5,114,0,0,887,888,5,101,0,0,888,889,5,102,0,0,889, - 162,1,0,0,0,890,891,5,111,0,0,891,892,5,112,0,0,892,893,5,116,0, - 0,893,894,5,105,0,0,894,895,5,111,0,0,895,896,5,110,0,0,896,897, - 5,97,0,0,897,898,5,108,0,0,898,164,1,0,0,0,899,900,5,108,0,0,900, - 901,5,105,0,0,901,902,5,115,0,0,902,903,5,116,0,0,903,166,1,0,0, - 0,904,905,5,114,0,0,905,906,5,101,0,0,906,907,5,99,0,0,907,908,5, - 111,0,0,908,909,5,114,0,0,909,910,5,100,0,0,910,168,1,0,0,0,911, - 912,5,98,0,0,912,913,5,111,0,0,913,914,5,111,0,0,914,915,5,108,0, - 0,915,170,1,0,0,0,916,917,5,98,0,0,917,918,5,121,0,0,918,919,5,116, - 0,0,919,920,5,101,0,0,920,921,5,115,0,0,921,172,1,0,0,0,922,923, - 5,100,0,0,923,924,5,111,0,0,924,925,5,117,0,0,925,926,5,98,0,0,926, - 927,5,108,0,0,927,928,5,101,0,0,928,174,1,0,0,0,929,930,5,105,0, - 0,930,931,5,110,0,0,931,932,5,116,0,0,932,933,5,51,0,0,933,934,5, - 50,0,0,934,176,1,0,0,0,935,936,5,105,0,0,936,937,5,110,0,0,937,938, - 5,116,0,0,938,939,5,54,0,0,939,940,5,52,0,0,940,178,1,0,0,0,941, - 942,5,115,0,0,942,943,5,116,0,0,943,944,5,114,0,0,944,945,5,105, - 0,0,945,946,5,110,0,0,946,947,5,103,0,0,947,180,1,0,0,0,948,949, - 5,117,0,0,949,950,5,105,0,0,950,951,5,110,0,0,951,952,5,116,0,0, - 952,953,5,51,0,0,953,954,5,50,0,0,954,182,1,0,0,0,955,956,5,117, - 0,0,956,957,5,105,0,0,957,958,5,110,0,0,958,959,5,116,0,0,959,960, - 5,54,0,0,960,961,5,52,0,0,961,184,1,0,0,0,962,963,5,116,0,0,963, - 964,5,114,0,0,964,965,5,117,0,0,965,966,5,101,0,0,966,186,1,0,0, - 0,967,968,5,102,0,0,968,969,5,97,0,0,969,970,5,108,0,0,970,971,5, - 115,0,0,971,972,5,101,0,0,972,188,1,0,0,0,973,974,5,110,0,0,974, - 975,5,117,0,0,975,976,5,108,0,0,976,977,5,108,0,0,977,190,1,0,0, - 0,978,979,5,45,0,0,979,980,5,62,0,0,980,192,1,0,0,0,981,982,5,58, - 0,0,982,194,1,0,0,0,983,984,5,59,0,0,984,196,1,0,0,0,985,986,5,44, - 0,0,986,198,1,0,0,0,987,988,5,46,0,0,988,200,1,0,0,0,989,990,5,123, - 0,0,990,202,1,0,0,0,991,992,5,125,0,0,992,204,1,0,0,0,993,994,5, - 91,0,0,994,206,1,0,0,0,995,996,5,93,0,0,996,208,1,0,0,0,997,998, - 5,40,0,0,998,210,1,0,0,0,999,1000,5,41,0,0,1000,212,1,0,0,0,1001, - 1002,5,60,0,0,1002,214,1,0,0,0,1003,1004,5,62,0,0,1004,216,1,0,0, - 0,1005,1006,5,38,0,0,1006,218,1,0,0,0,1007,1008,5,61,0,0,1008,220, - 1,0,0,0,1009,1011,5,45,0,0,1010,1009,1,0,0,0,1010,1011,1,0,0,0,1011, - 1013,1,0,0,0,1012,1014,7,0,0,0,1013,1012,1,0,0,0,1014,1015,1,0,0, - 0,1015,1013,1,0,0,0,1015,1016,1,0,0,0,1016,222,1,0,0,0,1017,1019, - 5,45,0,0,1018,1017,1,0,0,0,1018,1019,1,0,0,0,1019,1028,1,0,0,0,1020, - 1029,5,48,0,0,1021,1025,7,1,0,0,1022,1024,7,0,0,0,1023,1022,1,0, - 0,0,1024,1027,1,0,0,0,1025,1023,1,0,0,0,1025,1026,1,0,0,0,1026,1029, - 1,0,0,0,1027,1025,1,0,0,0,1028,1020,1,0,0,0,1028,1021,1,0,0,0,1029, - 1036,1,0,0,0,1030,1032,5,46,0,0,1031,1033,7,0,0,0,1032,1031,1,0, - 0,0,1033,1034,1,0,0,0,1034,1032,1,0,0,0,1034,1035,1,0,0,0,1035,1037, - 1,0,0,0,1036,1030,1,0,0,0,1036,1037,1,0,0,0,1037,1047,1,0,0,0,1038, - 1040,7,2,0,0,1039,1041,7,3,0,0,1040,1039,1,0,0,0,1040,1041,1,0,0, - 0,1041,1043,1,0,0,0,1042,1044,7,0,0,0,1043,1042,1,0,0,0,1044,1045, - 1,0,0,0,1045,1043,1,0,0,0,1045,1046,1,0,0,0,1046,1048,1,0,0,0,1047, - 1038,1,0,0,0,1047,1048,1,0,0,0,1048,224,1,0,0,0,1049,1053,7,4,0, - 0,1050,1052,7,5,0,0,1051,1050,1,0,0,0,1052,1055,1,0,0,0,1053,1051, - 1,0,0,0,1053,1054,1,0,0,0,1054,226,1,0,0,0,1055,1053,1,0,0,0,1056, - 1061,5,34,0,0,1057,1060,3,229,114,0,1058,1060,8,6,0,0,1059,1057, - 1,0,0,0,1059,1058,1,0,0,0,1060,1063,1,0,0,0,1061,1059,1,0,0,0,1061, - 1062,1,0,0,0,1062,1064,1,0,0,0,1063,1061,1,0,0,0,1064,1065,5,34, - 0,0,1065,228,1,0,0,0,1066,1074,5,92,0,0,1067,1075,7,7,0,0,1068,1069, - 5,117,0,0,1069,1070,3,231,115,0,1070,1071,3,231,115,0,1071,1072, - 3,231,115,0,1072,1073,3,231,115,0,1073,1075,1,0,0,0,1074,1067,1, - 0,0,0,1074,1068,1,0,0,0,1075,230,1,0,0,0,1076,1077,7,8,0,0,1077, - 232,1,0,0,0,1078,1079,5,47,0,0,1079,1080,5,47,0,0,1080,1084,1,0, - 0,0,1081,1083,8,9,0,0,1082,1081,1,0,0,0,1083,1086,1,0,0,0,1084,1082, - 1,0,0,0,1084,1085,1,0,0,0,1085,1087,1,0,0,0,1086,1084,1,0,0,0,1087, - 1088,6,116,0,0,1088,234,1,0,0,0,1089,1090,5,47,0,0,1090,1091,5,42, - 0,0,1091,1095,1,0,0,0,1092,1094,9,0,0,0,1093,1092,1,0,0,0,1094,1097, - 1,0,0,0,1095,1096,1,0,0,0,1095,1093,1,0,0,0,1096,1098,1,0,0,0,1097, - 1095,1,0,0,0,1098,1099,5,42,0,0,1099,1100,5,47,0,0,1100,1101,1,0, - 0,0,1101,1102,6,117,0,0,1102,236,1,0,0,0,1103,1105,7,10,0,0,1104, - 1103,1,0,0,0,1105,1106,1,0,0,0,1106,1104,1,0,0,0,1106,1107,1,0,0, - 0,1107,1108,1,0,0,0,1108,1109,6,118,0,0,1109,238,1,0,0,0,18,0,1010, - 1015,1018,1025,1028,1034,1036,1040,1045,1047,1053,1059,1061,1074, - 1084,1095,1106,1,0,1,0 + 7,114,2,115,7,115,2,116,7,116,2,117,7,117,2,118,7,118,2,119,7,119, + 1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,2, + 1,2,1,2,1,2,1,2,1,2,1,2,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,4, + 1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,5,1,5,1,5,1,5,1,6,1,6, + 1,6,1,6,1,6,1,6,1,6,1,6,1,6,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,8,1,8, + 1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,9,1,9,1,9,1,9,1,9,1,10,1,10,1,10,1, + 10,1,10,1,10,1,10,1,10,1,10,1,10,1,11,1,11,1,11,1,11,1,11,1,11,1, + 11,1,11,1,11,1,11,1,11,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1, + 13,1,13,1,13,1,13,1,13,1,13,1,14,1,14,1,14,1,14,1,14,1,14,1,14,1, + 14,1,14,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,16,1, + 16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,17,1,17,1,17,1,17,1,17,1, + 17,1,17,1,17,1,17,1,17,1,17,1,17,1,18,1,18,1,18,1,18,1,18,1,18,1, + 18,1,18,1,18,1,18,1,18,1,19,1,19,1,19,1,19,1,19,1,19,1,20,1,20,1, + 20,1,20,1,20,1,20,1,20,1,20,1,21,1,21,1,21,1,22,1,22,1,22,1,22,1, + 22,1,23,1,23,1,23,1,23,1,23,1,23,1,23,1,24,1,24,1,24,1,25,1,25,1, + 25,1,25,1,25,1,25,1,25,1,25,1,26,1,26,1,26,1,26,1,26,1,26,1,26,1, + 27,1,27,1,27,1,27,1,27,1,27,1,28,1,28,1,28,1,28,1,28,1,29,1,29,1, + 29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,30,1,30,1,30,1,30,1, + 30,1,31,1,31,1,31,1,31,1,31,1,31,1,32,1,32,1,32,1,32,1,33,1,33,1, + 33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,34,1,34,1,34,1, + 35,1,35,1,35,1,35,1,35,1,35,1,35,1,36,1,36,1,36,1,37,1,37,1,37,1, + 37,1,37,1,37,1,37,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,39,1, + 39,1,39,1,39,1,39,1,39,1,39,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1, + 40,1,40,1,40,1,40,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,42,1,42,1, + 42,1,42,1,42,1,42,1,42,1,42,1,42,1,43,1,43,1,43,1,43,1,43,1,43,1, + 43,1,43,1,43,1,43,1,43,1,43,1,43,1,43,1,43,1,44,1,44,1,44,1,44,1, + 44,1,44,1,44,1,44,1,44,1,44,1,45,1,45,1,45,1,45,1,45,1,45,1,45,1, + 45,1,45,1,45,1,45,1,45,1,45,1,46,1,46,1,46,1,46,1,46,1,46,1,47,1, + 47,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1, + 47,1,47,1,47,1,48,1,48,1,48,1,49,1,49,1,49,1,49,1,50,1,50,1,50,1, + 50,1,50,1,51,1,51,1,51,1,51,1,51,1,51,1,52,1,52,1,52,1,52,1,52,1, + 52,1,52,1,52,1,52,1,53,1,53,1,53,1,53,1,53,1,53,1,53,1,53,1,53,1, + 54,1,54,1,54,1,54,1,55,1,55,1,55,1,55,1,56,1,56,1,56,1,56,1,57,1, + 57,1,57,1,57,1,57,1,57,1,58,1,58,1,58,1,58,1,58,1,58,1,59,1,59,1, + 59,1,59,1,59,1,60,1,60,1,60,1,60,1,60,1,61,1,61,1,61,1,61,1,61,1, + 61,1,62,1,62,1,62,1,62,1,62,1,62,1,62,1,62,1,63,1,63,1,63,1,63,1, + 63,1,63,1,63,1,63,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1, + 64,1,64,1,65,1,65,1,65,1,65,1,65,1,66,1,66,1,66,1,66,1,66,1,66,1, + 66,1,66,1,66,1,66,1,66,1,66,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1, + 67,1,67,1,67,1,67,1,68,1,68,1,68,1,68,1,68,1,68,1,68,1,68,1,68,1, + 68,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1, + 70,1,70,1,70,1,70,1,70,1,70,1,70,1,70,1,70,1,70,1,70,1,70,1,70,1, + 70,1,70,1,70,1,70,1,70,1,70,1,70,1,71,1,71,1,71,1,71,1,71,1,72,1, + 72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,73,1, + 73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,74,1,74,1, + 74,1,74,1,74,1,74,1,74,1,74,1,74,1,74,1,74,1,74,1,75,1,75,1,75,1, + 75,1,75,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,76,1,77,1,77,1,77,1, + 77,1,77,1,78,1,78,1,78,1,78,1,78,1,78,1,78,1,78,1,78,1,78,1,78,1, + 78,1,78,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,79,1,80,1,80,1,80,1, + 80,1,80,1,80,1,80,1,80,1,80,1,81,1,81,1,81,1,81,1,81,1,81,1,81,1, + 81,1,81,1,81,1,81,1,81,1,81,1,81,1,82,1,82,1,82,1,82,1,82,1,82,1, + 82,1,82,1,82,1,83,1,83,1,83,1,83,1,83,1,84,1,84,1,84,1,84,1,84,1, + 84,1,84,1,85,1,85,1,85,1,85,1,85,1,86,1,86,1,86,1,86,1,86,1,86,1, + 87,1,87,1,87,1,87,1,87,1,87,1,87,1,88,1,88,1,88,1,88,1,88,1,88,1, + 89,1,89,1,89,1,89,1,89,1,89,1,90,1,90,1,90,1,90,1,90,1,90,1,90,1, + 91,1,91,1,91,1,91,1,91,1,91,1,91,1,92,1,92,1,92,1,92,1,92,1,92,1, + 92,1,93,1,93,1,93,1,93,1,93,1,94,1,94,1,94,1,94,1,94,1,94,1,95,1, + 95,1,95,1,95,1,95,1,96,1,96,1,96,1,97,1,97,1,98,1,98,1,99,1,99,1, + 100,1,100,1,101,1,101,1,102,1,102,1,103,1,103,1,104,1,104,1,105, + 1,105,1,106,1,106,1,107,1,107,1,108,1,108,1,109,1,109,1,110,1,110, + 1,111,3,111,1020,8,111,1,111,4,111,1023,8,111,11,111,12,111,1024, + 1,112,3,112,1028,8,112,1,112,1,112,1,112,5,112,1033,8,112,10,112, + 12,112,1036,9,112,3,112,1038,8,112,1,112,1,112,4,112,1042,8,112, + 11,112,12,112,1043,3,112,1046,8,112,1,112,1,112,3,112,1050,8,112, + 1,112,4,112,1053,8,112,11,112,12,112,1054,3,112,1057,8,112,1,113, + 1,113,5,113,1061,8,113,10,113,12,113,1064,9,113,1,114,1,114,1,114, + 5,114,1069,8,114,10,114,12,114,1072,9,114,1,114,1,114,1,115,1,115, + 1,115,1,115,1,115,1,115,1,115,1,115,3,115,1084,8,115,1,116,1,116, + 1,117,1,117,1,117,1,117,5,117,1092,8,117,10,117,12,117,1095,9,117, + 1,117,1,117,1,118,1,118,1,118,1,118,5,118,1103,8,118,10,118,12,118, + 1106,9,118,1,118,1,118,1,118,1,118,1,118,1,119,4,119,1114,8,119, + 11,119,12,119,1115,1,119,1,119,1,1104,0,120,1,1,3,2,5,3,7,4,9,5, + 11,6,13,7,15,8,17,9,19,10,21,11,23,12,25,13,27,14,29,15,31,16,33, + 17,35,18,37,19,39,20,41,21,43,22,45,23,47,24,49,25,51,26,53,27,55, + 28,57,29,59,30,61,31,63,32,65,33,67,34,69,35,71,36,73,37,75,38,77, + 39,79,40,81,41,83,42,85,43,87,44,89,45,91,46,93,47,95,48,97,49,99, + 50,101,51,103,52,105,53,107,54,109,55,111,56,113,57,115,58,117,59, + 119,60,121,61,123,62,125,63,127,64,129,65,131,66,133,67,135,68,137, + 69,139,70,141,71,143,72,145,73,147,74,149,75,151,76,153,77,155,78, + 157,79,159,80,161,81,163,82,165,83,167,84,169,85,171,86,173,87,175, + 88,177,89,179,90,181,91,183,92,185,93,187,94,189,95,191,96,193,97, + 195,98,197,99,199,100,201,101,203,102,205,103,207,104,209,105,211, + 106,213,107,215,108,217,109,219,110,221,111,223,112,225,113,227, + 114,229,115,231,0,233,0,235,116,237,117,239,118,1,0,11,1,0,48,57, + 1,0,49,57,2,0,69,69,101,101,2,0,43,43,45,45,3,0,65,90,95,95,97,122, + 4,0,48,57,65,90,95,95,97,122,4,0,10,10,13,13,34,34,92,92,8,0,34, + 34,47,47,92,92,98,98,102,102,110,110,114,114,116,116,3,0,48,57,65, + 70,97,102,2,0,10,10,13,13,3,0,9,10,13,13,32,32,1133,0,1,1,0,0,0, + 0,3,1,0,0,0,0,5,1,0,0,0,0,7,1,0,0,0,0,9,1,0,0,0,0,11,1,0,0,0,0,13, + 1,0,0,0,0,15,1,0,0,0,0,17,1,0,0,0,0,19,1,0,0,0,0,21,1,0,0,0,0,23, + 1,0,0,0,0,25,1,0,0,0,0,27,1,0,0,0,0,29,1,0,0,0,0,31,1,0,0,0,0,33, + 1,0,0,0,0,35,1,0,0,0,0,37,1,0,0,0,0,39,1,0,0,0,0,41,1,0,0,0,0,43, + 1,0,0,0,0,45,1,0,0,0,0,47,1,0,0,0,0,49,1,0,0,0,0,51,1,0,0,0,0,53, + 1,0,0,0,0,55,1,0,0,0,0,57,1,0,0,0,0,59,1,0,0,0,0,61,1,0,0,0,0,63, + 1,0,0,0,0,65,1,0,0,0,0,67,1,0,0,0,0,69,1,0,0,0,0,71,1,0,0,0,0,73, + 1,0,0,0,0,75,1,0,0,0,0,77,1,0,0,0,0,79,1,0,0,0,0,81,1,0,0,0,0,83, + 1,0,0,0,0,85,1,0,0,0,0,87,1,0,0,0,0,89,1,0,0,0,0,91,1,0,0,0,0,93, + 1,0,0,0,0,95,1,0,0,0,0,97,1,0,0,0,0,99,1,0,0,0,0,101,1,0,0,0,0,103, + 1,0,0,0,0,105,1,0,0,0,0,107,1,0,0,0,0,109,1,0,0,0,0,111,1,0,0,0, + 0,113,1,0,0,0,0,115,1,0,0,0,0,117,1,0,0,0,0,119,1,0,0,0,0,121,1, + 0,0,0,0,123,1,0,0,0,0,125,1,0,0,0,0,127,1,0,0,0,0,129,1,0,0,0,0, + 131,1,0,0,0,0,133,1,0,0,0,0,135,1,0,0,0,0,137,1,0,0,0,0,139,1,0, + 0,0,0,141,1,0,0,0,0,143,1,0,0,0,0,145,1,0,0,0,0,147,1,0,0,0,0,149, + 1,0,0,0,0,151,1,0,0,0,0,153,1,0,0,0,0,155,1,0,0,0,0,157,1,0,0,0, + 0,159,1,0,0,0,0,161,1,0,0,0,0,163,1,0,0,0,0,165,1,0,0,0,0,167,1, + 0,0,0,0,169,1,0,0,0,0,171,1,0,0,0,0,173,1,0,0,0,0,175,1,0,0,0,0, + 177,1,0,0,0,0,179,1,0,0,0,0,181,1,0,0,0,0,183,1,0,0,0,0,185,1,0, + 0,0,0,187,1,0,0,0,0,189,1,0,0,0,0,191,1,0,0,0,0,193,1,0,0,0,0,195, + 1,0,0,0,0,197,1,0,0,0,0,199,1,0,0,0,0,201,1,0,0,0,0,203,1,0,0,0, + 0,205,1,0,0,0,0,207,1,0,0,0,0,209,1,0,0,0,0,211,1,0,0,0,0,213,1, + 0,0,0,0,215,1,0,0,0,0,217,1,0,0,0,0,219,1,0,0,0,0,221,1,0,0,0,0, + 223,1,0,0,0,0,225,1,0,0,0,0,227,1,0,0,0,0,229,1,0,0,0,0,235,1,0, + 0,0,0,237,1,0,0,0,0,239,1,0,0,0,1,241,1,0,0,0,3,251,1,0,0,0,5,256, + 1,0,0,0,7,263,1,0,0,0,9,272,1,0,0,0,11,283,1,0,0,0,13,287,1,0,0, + 0,15,296,1,0,0,0,17,303,1,0,0,0,19,312,1,0,0,0,21,317,1,0,0,0,23, + 327,1,0,0,0,25,338,1,0,0,0,27,346,1,0,0,0,29,352,1,0,0,0,31,361, + 1,0,0,0,33,371,1,0,0,0,35,380,1,0,0,0,37,392,1,0,0,0,39,403,1,0, + 0,0,41,409,1,0,0,0,43,417,1,0,0,0,45,420,1,0,0,0,47,425,1,0,0,0, + 49,432,1,0,0,0,51,435,1,0,0,0,53,443,1,0,0,0,55,450,1,0,0,0,57,456, + 1,0,0,0,59,461,1,0,0,0,61,472,1,0,0,0,63,477,1,0,0,0,65,483,1,0, + 0,0,67,487,1,0,0,0,69,499,1,0,0,0,71,502,1,0,0,0,73,509,1,0,0,0, + 75,512,1,0,0,0,77,519,1,0,0,0,79,527,1,0,0,0,81,534,1,0,0,0,83,545, + 1,0,0,0,85,552,1,0,0,0,87,561,1,0,0,0,89,576,1,0,0,0,91,586,1,0, + 0,0,93,599,1,0,0,0,95,605,1,0,0,0,97,622,1,0,0,0,99,625,1,0,0,0, + 101,629,1,0,0,0,103,634,1,0,0,0,105,640,1,0,0,0,107,649,1,0,0,0, + 109,658,1,0,0,0,111,662,1,0,0,0,113,666,1,0,0,0,115,670,1,0,0,0, + 117,676,1,0,0,0,119,682,1,0,0,0,121,687,1,0,0,0,123,692,1,0,0,0, + 125,698,1,0,0,0,127,706,1,0,0,0,129,714,1,0,0,0,131,725,1,0,0,0, + 133,730,1,0,0,0,135,742,1,0,0,0,137,753,1,0,0,0,139,763,1,0,0,0, + 141,775,1,0,0,0,143,795,1,0,0,0,145,800,1,0,0,0,147,813,1,0,0,0, + 149,825,1,0,0,0,151,837,1,0,0,0,153,842,1,0,0,0,155,850,1,0,0,0, + 157,855,1,0,0,0,159,868,1,0,0,0,161,876,1,0,0,0,163,885,1,0,0,0, + 165,899,1,0,0,0,167,908,1,0,0,0,169,913,1,0,0,0,171,920,1,0,0,0, + 173,925,1,0,0,0,175,931,1,0,0,0,177,938,1,0,0,0,179,944,1,0,0,0, + 181,950,1,0,0,0,183,957,1,0,0,0,185,964,1,0,0,0,187,971,1,0,0,0, + 189,976,1,0,0,0,191,982,1,0,0,0,193,987,1,0,0,0,195,990,1,0,0,0, + 197,992,1,0,0,0,199,994,1,0,0,0,201,996,1,0,0,0,203,998,1,0,0,0, + 205,1000,1,0,0,0,207,1002,1,0,0,0,209,1004,1,0,0,0,211,1006,1,0, + 0,0,213,1008,1,0,0,0,215,1010,1,0,0,0,217,1012,1,0,0,0,219,1014, + 1,0,0,0,221,1016,1,0,0,0,223,1019,1,0,0,0,225,1027,1,0,0,0,227,1058, + 1,0,0,0,229,1065,1,0,0,0,231,1075,1,0,0,0,233,1085,1,0,0,0,235,1087, + 1,0,0,0,237,1098,1,0,0,0,239,1113,1,0,0,0,241,242,5,119,0,0,242, + 243,5,111,0,0,243,244,5,114,0,0,244,245,5,107,0,0,245,246,5,115, + 0,0,246,247,5,112,0,0,247,248,5,97,0,0,248,249,5,99,0,0,249,250, + 5,101,0,0,250,2,1,0,0,0,251,252,5,116,0,0,252,253,5,121,0,0,253, + 254,5,112,0,0,254,255,5,101,0,0,255,4,1,0,0,0,256,257,5,111,0,0, + 257,258,5,98,0,0,258,259,5,106,0,0,259,260,5,101,0,0,260,261,5,99, + 0,0,261,262,5,116,0,0,262,6,1,0,0,0,263,264,5,115,0,0,264,265,5, + 116,0,0,265,266,5,111,0,0,266,267,5,114,0,0,267,268,5,97,0,0,268, + 269,5,98,0,0,269,270,5,108,0,0,270,271,5,101,0,0,271,8,1,0,0,0,272, + 273,5,105,0,0,273,274,5,109,0,0,274,275,5,112,0,0,275,276,5,108, + 0,0,276,277,5,101,0,0,277,278,5,109,0,0,278,279,5,101,0,0,279,280, + 5,110,0,0,280,281,5,116,0,0,281,282,5,115,0,0,282,10,1,0,0,0,283, + 284,5,114,0,0,284,285,5,101,0,0,285,286,5,102,0,0,286,12,1,0,0,0, + 287,288,5,102,0,0,288,289,5,114,0,0,289,290,5,97,0,0,290,291,5,103, + 0,0,291,292,5,109,0,0,292,293,5,101,0,0,293,294,5,110,0,0,294,295, + 5,116,0,0,295,14,1,0,0,0,296,297,5,105,0,0,297,298,5,109,0,0,298, + 299,5,112,0,0,299,300,5,111,0,0,300,301,5,114,0,0,301,302,5,116, + 0,0,302,16,1,0,0,0,303,304,5,101,0,0,304,305,5,120,0,0,305,306,5, + 116,0,0,306,307,5,101,0,0,307,308,5,114,0,0,308,309,5,110,0,0,309, + 310,5,97,0,0,310,311,5,108,0,0,311,18,1,0,0,0,312,313,5,97,0,0,313, + 314,5,116,0,0,314,315,5,111,0,0,315,316,5,109,0,0,316,20,1,0,0,0, + 317,318,5,105,0,0,318,319,5,110,0,0,319,320,5,116,0,0,320,321,5, + 101,0,0,321,322,5,114,0,0,322,323,5,102,0,0,323,324,5,97,0,0,324, + 325,5,99,0,0,325,326,5,101,0,0,326,22,1,0,0,0,327,328,5,105,0,0, + 328,329,5,110,0,0,329,330,5,116,0,0,330,331,5,101,0,0,331,332,5, + 114,0,0,332,333,5,102,0,0,333,334,5,97,0,0,334,335,5,99,0,0,335, + 336,5,101,0,0,336,337,5,115,0,0,337,24,1,0,0,0,338,339,5,112,0,0, + 339,340,5,97,0,0,340,341,5,99,0,0,341,342,5,107,0,0,342,343,5,97, + 0,0,343,344,5,103,0,0,344,345,5,101,0,0,345,26,1,0,0,0,346,347,5, + 118,0,0,347,348,5,97,0,0,348,349,5,108,0,0,349,350,5,117,0,0,350, + 351,5,101,0,0,351,28,1,0,0,0,352,353,5,114,0,0,353,354,5,101,0,0, + 354,355,5,108,0,0,355,356,5,97,0,0,356,357,5,116,0,0,357,358,5,105, + 0,0,358,359,5,111,0,0,359,360,5,110,0,0,360,30,1,0,0,0,361,362,5, + 111,0,0,362,363,5,112,0,0,363,364,5,101,0,0,364,365,5,114,0,0,365, + 366,5,97,0,0,366,367,5,116,0,0,367,368,5,105,0,0,368,369,5,111,0, + 0,369,370,5,110,0,0,370,32,1,0,0,0,371,372,5,102,0,0,372,373,5,117, + 0,0,373,374,5,110,0,0,374,375,5,99,0,0,375,376,5,116,0,0,376,377, + 5,105,0,0,377,378,5,111,0,0,378,379,5,110,0,0,379,34,1,0,0,0,380, + 381,5,99,0,0,381,382,5,111,0,0,382,383,5,110,0,0,383,384,5,115,0, + 0,384,385,5,116,0,0,385,386,5,114,0,0,386,387,5,117,0,0,387,388, + 5,99,0,0,388,389,5,116,0,0,389,390,5,111,0,0,390,391,5,114,0,0,391, + 36,1,0,0,0,392,393,5,99,0,0,393,394,5,111,0,0,394,395,5,110,0,0, + 395,396,5,115,0,0,396,397,5,116,0,0,397,398,5,114,0,0,398,399,5, + 117,0,0,399,400,5,99,0,0,400,401,5,116,0,0,401,402,5,115,0,0,402, + 38,1,0,0,0,403,404,5,105,0,0,404,405,5,110,0,0,405,406,5,112,0,0, + 406,407,5,117,0,0,407,408,5,116,0,0,408,40,1,0,0,0,409,410,5,99, + 0,0,410,411,5,111,0,0,411,412,5,110,0,0,412,413,5,102,0,0,413,414, + 5,111,0,0,414,415,5,114,0,0,415,416,5,109,0,0,416,42,1,0,0,0,417, + 418,5,97,0,0,418,419,5,115,0,0,419,44,1,0,0,0,420,421,5,98,0,0,421, + 422,5,105,0,0,422,423,5,110,0,0,423,424,5,100,0,0,424,46,1,0,0,0, + 425,426,5,115,0,0,426,427,5,116,0,0,427,428,5,97,0,0,428,429,5,116, + 0,0,429,430,5,105,0,0,430,431,5,99,0,0,431,48,1,0,0,0,432,433,5, + 116,0,0,433,434,5,111,0,0,434,50,1,0,0,0,435,436,5,112,0,0,436,437, + 5,114,0,0,437,438,5,105,0,0,438,439,5,118,0,0,439,440,5,97,0,0,440, + 441,5,116,0,0,441,442,5,101,0,0,442,52,1,0,0,0,443,444,5,115,0,0, + 444,445,5,104,0,0,445,446,5,97,0,0,446,447,5,114,0,0,447,448,5,101, + 0,0,448,449,5,100,0,0,449,54,1,0,0,0,450,451,5,115,0,0,451,452,5, + 116,0,0,452,453,5,97,0,0,453,454,5,116,0,0,454,455,5,101,0,0,455, + 56,1,0,0,0,456,457,5,101,0,0,457,458,5,100,0,0,458,459,5,103,0,0, + 459,460,5,101,0,0,460,58,1,0,0,0,461,462,5,112,0,0,462,463,5,114, + 0,0,463,464,5,111,0,0,464,465,5,106,0,0,465,466,5,101,0,0,466,467, + 5,99,0,0,467,468,5,116,0,0,468,469,5,105,0,0,469,470,5,111,0,0,470, + 471,5,110,0,0,471,60,1,0,0,0,472,473,5,119,0,0,473,474,5,105,0,0, + 474,475,5,116,0,0,475,476,5,104,0,0,476,62,1,0,0,0,477,478,5,117, + 0,0,478,479,5,115,0,0,479,480,5,105,0,0,480,481,5,110,0,0,481,482, + 5,103,0,0,482,64,1,0,0,0,483,484,5,118,0,0,484,485,5,105,0,0,485, + 486,5,97,0,0,486,66,1,0,0,0,487,488,5,109,0,0,488,489,5,97,0,0,489, + 490,5,116,0,0,490,491,5,101,0,0,491,492,5,114,0,0,492,493,5,105, + 0,0,493,494,5,97,0,0,494,495,5,108,0,0,495,496,5,105,0,0,496,497, + 5,122,0,0,497,498,5,101,0,0,498,68,1,0,0,0,499,500,5,105,0,0,500, + 501,5,102,0,0,501,70,1,0,0,0,502,503,5,97,0,0,503,504,5,98,0,0,504, + 505,5,115,0,0,505,506,5,101,0,0,506,507,5,110,0,0,507,508,5,116, + 0,0,508,72,1,0,0,0,509,510,5,111,0,0,510,511,5,110,0,0,511,74,1, + 0,0,0,512,513,5,112,0,0,513,514,5,111,0,0,514,515,5,108,0,0,515, + 516,5,105,0,0,516,517,5,99,0,0,517,518,5,121,0,0,518,76,1,0,0,0, + 519,520,5,100,0,0,520,521,5,101,0,0,521,522,5,102,0,0,522,523,5, + 97,0,0,523,524,5,117,0,0,524,525,5,108,0,0,525,526,5,116,0,0,526, + 78,1,0,0,0,527,528,5,115,0,0,528,529,5,111,0,0,529,530,5,117,0,0, + 530,531,5,114,0,0,531,532,5,99,0,0,532,533,5,101,0,0,533,80,1,0, + 0,0,534,535,5,114,0,0,535,536,5,101,0,0,536,537,5,112,0,0,537,538, + 5,111,0,0,538,539,5,115,0,0,539,540,5,105,0,0,540,541,5,116,0,0, + 541,542,5,111,0,0,542,543,5,114,0,0,543,544,5,121,0,0,544,82,1,0, + 0,0,545,546,5,99,0,0,546,547,5,111,0,0,547,548,5,109,0,0,548,549, + 5,109,0,0,549,550,5,105,0,0,550,551,5,116,0,0,551,84,1,0,0,0,552, + 553,5,114,0,0,553,554,5,101,0,0,554,555,5,118,0,0,555,556,5,105, + 0,0,556,557,5,115,0,0,557,558,5,105,0,0,558,559,5,111,0,0,559,560, + 5,110,0,0,560,86,1,0,0,0,561,562,5,115,0,0,562,563,5,101,0,0,563, + 564,5,109,0,0,564,565,5,97,0,0,565,566,5,110,0,0,566,567,5,116,0, + 0,567,568,5,105,0,0,568,569,5,99,0,0,569,570,5,45,0,0,570,571,5, + 109,0,0,571,572,5,97,0,0,572,573,5,106,0,0,573,574,5,111,0,0,574, + 575,5,114,0,0,575,88,1,0,0,0,576,577,5,111,0,0,577,578,5,110,0,0, + 578,579,5,45,0,0,579,580,5,100,0,0,580,581,5,101,0,0,581,582,5,108, + 0,0,582,583,5,101,0,0,583,584,5,116,0,0,584,585,5,101,0,0,585,90, + 1,0,0,0,586,587,5,114,0,0,587,588,5,101,0,0,588,589,5,116,0,0,589, + 590,5,97,0,0,590,591,5,105,0,0,591,592,5,110,0,0,592,593,5,45,0, + 0,593,594,5,111,0,0,594,595,5,116,0,0,595,596,5,104,0,0,596,597, + 5,101,0,0,597,598,5,114,0,0,598,92,1,0,0,0,599,600,5,107,0,0,600, + 601,5,101,0,0,601,602,5,121,0,0,602,603,5,101,0,0,603,604,5,100, + 0,0,604,94,1,0,0,0,605,606,5,112,0,0,606,607,5,117,0,0,607,608,5, + 98,0,0,608,609,5,108,0,0,609,610,5,105,0,0,610,611,5,99,0,0,611, + 612,5,45,0,0,612,613,5,116,0,0,613,614,5,114,0,0,614,615,5,97,0, + 0,615,616,5,118,0,0,616,617,5,101,0,0,617,618,5,114,0,0,618,619, + 5,115,0,0,619,620,5,97,0,0,620,621,5,108,0,0,621,96,1,0,0,0,622, + 623,5,105,0,0,623,624,5,100,0,0,624,98,1,0,0,0,625,626,5,100,0,0, + 626,627,5,111,0,0,627,628,5,99,0,0,628,100,1,0,0,0,629,630,5,109, + 0,0,630,631,5,111,0,0,631,632,5,100,0,0,632,633,5,101,0,0,633,102, + 1,0,0,0,634,635,5,101,0,0,635,636,5,109,0,0,636,637,5,105,0,0,637, + 638,5,116,0,0,638,639,5,115,0,0,639,104,1,0,0,0,640,641,5,114,0, + 0,641,642,5,101,0,0,642,643,5,99,0,0,643,644,5,101,0,0,644,645,5, + 105,0,0,645,646,5,118,0,0,646,647,5,101,0,0,647,648,5,114,0,0,648, + 106,1,0,0,0,649,650,5,114,0,0,650,651,5,101,0,0,651,652,5,113,0, + 0,652,653,5,117,0,0,653,654,5,105,0,0,654,655,5,114,0,0,655,656, + 5,101,0,0,656,657,5,115,0,0,657,108,1,0,0,0,658,659,5,97,0,0,659, + 660,5,110,0,0,660,661,5,121,0,0,661,110,1,0,0,0,662,663,5,103,0, + 0,663,664,5,101,0,0,664,665,5,116,0,0,665,112,1,0,0,0,666,667,5, + 115,0,0,667,668,5,101,0,0,668,669,5,116,0,0,669,114,1,0,0,0,670, + 671,5,119,0,0,671,672,5,97,0,0,672,673,5,116,0,0,673,674,5,99,0, + 0,674,675,5,104,0,0,675,116,1,0,0,0,676,677,5,115,0,0,677,678,5, + 116,0,0,678,679,5,97,0,0,679,680,5,114,0,0,680,681,5,116,0,0,681, + 118,1,0,0,0,682,683,5,115,0,0,683,684,5,116,0,0,684,685,5,111,0, + 0,685,686,5,112,0,0,686,120,1,0,0,0,687,688,5,114,0,0,688,689,5, + 101,0,0,689,690,5,97,0,0,690,691,5,100,0,0,691,122,1,0,0,0,692,693, + 5,119,0,0,693,694,5,114,0,0,694,695,5,105,0,0,695,696,5,116,0,0, + 696,697,5,101,0,0,697,124,1,0,0,0,698,699,5,114,0,0,699,700,5,101, + 0,0,700,701,5,115,0,0,701,702,5,111,0,0,702,703,5,108,0,0,703,704, + 5,118,0,0,704,705,5,101,0,0,705,126,1,0,0,0,706,707,5,99,0,0,707, + 708,5,111,0,0,708,709,5,110,0,0,709,710,5,110,0,0,710,711,5,101, + 0,0,711,712,5,99,0,0,712,713,5,116,0,0,713,128,1,0,0,0,714,715,5, + 100,0,0,715,716,5,105,0,0,716,717,5,115,0,0,717,718,5,99,0,0,718, + 719,5,111,0,0,719,720,5,110,0,0,720,721,5,110,0,0,721,722,5,101, + 0,0,722,723,5,99,0,0,723,724,5,116,0,0,724,130,1,0,0,0,725,726,5, + 99,0,0,726,727,5,97,0,0,727,728,5,108,0,0,728,729,5,108,0,0,729, + 132,1,0,0,0,730,731,5,119,0,0,731,732,5,97,0,0,732,733,5,116,0,0, + 733,734,5,99,0,0,734,735,5,104,0,0,735,736,5,45,0,0,736,737,5,115, + 0,0,737,738,5,116,0,0,738,739,5,97,0,0,739,740,5,114,0,0,740,741, + 5,116,0,0,741,134,1,0,0,0,742,743,5,119,0,0,743,744,5,97,0,0,744, + 745,5,116,0,0,745,746,5,99,0,0,746,747,5,104,0,0,747,748,5,45,0, + 0,748,749,5,115,0,0,749,750,5,116,0,0,750,751,5,111,0,0,751,752, + 5,112,0,0,752,136,1,0,0,0,753,754,5,115,0,0,754,755,5,117,0,0,755, + 756,5,98,0,0,756,757,5,115,0,0,757,758,5,99,0,0,758,759,5,114,0, + 0,759,760,5,105,0,0,760,761,5,98,0,0,761,762,5,101,0,0,762,138,1, + 0,0,0,763,764,5,117,0,0,764,765,5,110,0,0,765,766,5,115,0,0,766, + 767,5,117,0,0,767,768,5,98,0,0,768,769,5,115,0,0,769,770,5,99,0, + 0,770,771,5,114,0,0,771,772,5,105,0,0,772,773,5,98,0,0,773,774,5, + 101,0,0,774,140,1,0,0,0,775,776,5,111,0,0,776,777,5,112,0,0,777, + 778,5,116,0,0,778,779,5,105,0,0,779,780,5,109,0,0,780,781,5,105, + 0,0,781,782,5,115,0,0,782,783,5,116,0,0,783,784,5,105,0,0,784,785, + 5,99,0,0,785,786,5,45,0,0,786,787,5,114,0,0,787,788,5,101,0,0,788, + 789,5,103,0,0,789,790,5,105,0,0,790,791,5,115,0,0,791,792,5,116, + 0,0,792,793,5,101,0,0,793,794,5,114,0,0,794,142,1,0,0,0,795,796, + 5,99,0,0,796,797,5,114,0,0,797,798,5,100,0,0,798,799,5,116,0,0,799, + 144,1,0,0,0,800,801,5,111,0,0,801,802,5,112,0,0,802,803,5,116,0, + 0,803,804,5,105,0,0,804,805,5,111,0,0,805,806,5,110,0,0,806,807, + 5,97,0,0,807,808,5,108,0,0,808,809,5,45,0,0,809,810,5,111,0,0,810, + 811,5,110,0,0,811,812,5,101,0,0,812,146,1,0,0,0,813,814,5,101,0, + 0,814,815,5,120,0,0,815,816,5,97,0,0,816,817,5,99,0,0,817,818,5, + 116,0,0,818,819,5,108,0,0,819,820,5,121,0,0,820,821,5,45,0,0,821, + 822,5,111,0,0,822,823,5,110,0,0,823,824,5,101,0,0,824,148,1,0,0, + 0,825,826,5,109,0,0,826,827,5,97,0,0,827,828,5,110,0,0,828,829,5, + 121,0,0,829,830,5,45,0,0,830,831,5,117,0,0,831,832,5,110,0,0,832, + 833,5,105,0,0,833,834,5,113,0,0,834,835,5,117,0,0,835,836,5,101, + 0,0,836,150,1,0,0,0,837,838,5,109,0,0,838,839,5,97,0,0,839,840,5, + 110,0,0,840,841,5,121,0,0,841,152,1,0,0,0,842,843,5,111,0,0,843, + 844,5,114,0,0,844,845,5,100,0,0,845,846,5,101,0,0,846,847,5,114, + 0,0,847,848,5,101,0,0,848,849,5,100,0,0,849,154,1,0,0,0,850,851, + 5,117,0,0,851,852,5,110,0,0,852,853,5,105,0,0,853,854,5,116,0,0, + 854,156,1,0,0,0,855,856,5,119,0,0,856,857,5,97,0,0,857,858,5,116, + 0,0,858,859,5,99,0,0,859,860,5,104,0,0,860,861,5,45,0,0,861,862, + 5,104,0,0,862,863,5,97,0,0,863,864,5,110,0,0,864,865,5,100,0,0,865, + 866,5,108,0,0,866,867,5,101,0,0,867,158,1,0,0,0,868,869,5,109,0, + 0,869,870,5,101,0,0,870,871,5,115,0,0,871,872,5,115,0,0,872,873, + 5,97,0,0,873,874,5,103,0,0,874,875,5,101,0,0,875,160,1,0,0,0,876, + 877,5,97,0,0,877,878,5,116,0,0,878,879,5,111,0,0,879,880,5,109,0, + 0,880,881,5,45,0,0,881,882,5,114,0,0,882,883,5,101,0,0,883,884,5, + 102,0,0,884,162,1,0,0,0,885,886,5,105,0,0,886,887,5,110,0,0,887, + 888,5,116,0,0,888,889,5,101,0,0,889,890,5,114,0,0,890,891,5,102, + 0,0,891,892,5,97,0,0,892,893,5,99,0,0,893,894,5,101,0,0,894,895, + 5,45,0,0,895,896,5,114,0,0,896,897,5,101,0,0,897,898,5,102,0,0,898, + 164,1,0,0,0,899,900,5,111,0,0,900,901,5,112,0,0,901,902,5,116,0, + 0,902,903,5,105,0,0,903,904,5,111,0,0,904,905,5,110,0,0,905,906, + 5,97,0,0,906,907,5,108,0,0,907,166,1,0,0,0,908,909,5,108,0,0,909, + 910,5,105,0,0,910,911,5,115,0,0,911,912,5,116,0,0,912,168,1,0,0, + 0,913,914,5,114,0,0,914,915,5,101,0,0,915,916,5,99,0,0,916,917,5, + 111,0,0,917,918,5,114,0,0,918,919,5,100,0,0,919,170,1,0,0,0,920, + 921,5,98,0,0,921,922,5,111,0,0,922,923,5,111,0,0,923,924,5,108,0, + 0,924,172,1,0,0,0,925,926,5,98,0,0,926,927,5,121,0,0,927,928,5,116, + 0,0,928,929,5,101,0,0,929,930,5,115,0,0,930,174,1,0,0,0,931,932, + 5,100,0,0,932,933,5,111,0,0,933,934,5,117,0,0,934,935,5,98,0,0,935, + 936,5,108,0,0,936,937,5,101,0,0,937,176,1,0,0,0,938,939,5,105,0, + 0,939,940,5,110,0,0,940,941,5,116,0,0,941,942,5,51,0,0,942,943,5, + 50,0,0,943,178,1,0,0,0,944,945,5,105,0,0,945,946,5,110,0,0,946,947, + 5,116,0,0,947,948,5,54,0,0,948,949,5,52,0,0,949,180,1,0,0,0,950, + 951,5,115,0,0,951,952,5,116,0,0,952,953,5,114,0,0,953,954,5,105, + 0,0,954,955,5,110,0,0,955,956,5,103,0,0,956,182,1,0,0,0,957,958, + 5,117,0,0,958,959,5,105,0,0,959,960,5,110,0,0,960,961,5,116,0,0, + 961,962,5,51,0,0,962,963,5,50,0,0,963,184,1,0,0,0,964,965,5,117, + 0,0,965,966,5,105,0,0,966,967,5,110,0,0,967,968,5,116,0,0,968,969, + 5,54,0,0,969,970,5,52,0,0,970,186,1,0,0,0,971,972,5,116,0,0,972, + 973,5,114,0,0,973,974,5,117,0,0,974,975,5,101,0,0,975,188,1,0,0, + 0,976,977,5,102,0,0,977,978,5,97,0,0,978,979,5,108,0,0,979,980,5, + 115,0,0,980,981,5,101,0,0,981,190,1,0,0,0,982,983,5,110,0,0,983, + 984,5,117,0,0,984,985,5,108,0,0,985,986,5,108,0,0,986,192,1,0,0, + 0,987,988,5,45,0,0,988,989,5,62,0,0,989,194,1,0,0,0,990,991,5,58, + 0,0,991,196,1,0,0,0,992,993,5,59,0,0,993,198,1,0,0,0,994,995,5,44, + 0,0,995,200,1,0,0,0,996,997,5,46,0,0,997,202,1,0,0,0,998,999,5,123, + 0,0,999,204,1,0,0,0,1000,1001,5,125,0,0,1001,206,1,0,0,0,1002,1003, + 5,91,0,0,1003,208,1,0,0,0,1004,1005,5,93,0,0,1005,210,1,0,0,0,1006, + 1007,5,40,0,0,1007,212,1,0,0,0,1008,1009,5,41,0,0,1009,214,1,0,0, + 0,1010,1011,5,60,0,0,1011,216,1,0,0,0,1012,1013,5,62,0,0,1013,218, + 1,0,0,0,1014,1015,5,38,0,0,1015,220,1,0,0,0,1016,1017,5,61,0,0,1017, + 222,1,0,0,0,1018,1020,5,45,0,0,1019,1018,1,0,0,0,1019,1020,1,0,0, + 0,1020,1022,1,0,0,0,1021,1023,7,0,0,0,1022,1021,1,0,0,0,1023,1024, + 1,0,0,0,1024,1022,1,0,0,0,1024,1025,1,0,0,0,1025,224,1,0,0,0,1026, + 1028,5,45,0,0,1027,1026,1,0,0,0,1027,1028,1,0,0,0,1028,1037,1,0, + 0,0,1029,1038,5,48,0,0,1030,1034,7,1,0,0,1031,1033,7,0,0,0,1032, + 1031,1,0,0,0,1033,1036,1,0,0,0,1034,1032,1,0,0,0,1034,1035,1,0,0, + 0,1035,1038,1,0,0,0,1036,1034,1,0,0,0,1037,1029,1,0,0,0,1037,1030, + 1,0,0,0,1038,1045,1,0,0,0,1039,1041,5,46,0,0,1040,1042,7,0,0,0,1041, + 1040,1,0,0,0,1042,1043,1,0,0,0,1043,1041,1,0,0,0,1043,1044,1,0,0, + 0,1044,1046,1,0,0,0,1045,1039,1,0,0,0,1045,1046,1,0,0,0,1046,1056, + 1,0,0,0,1047,1049,7,2,0,0,1048,1050,7,3,0,0,1049,1048,1,0,0,0,1049, + 1050,1,0,0,0,1050,1052,1,0,0,0,1051,1053,7,0,0,0,1052,1051,1,0,0, + 0,1053,1054,1,0,0,0,1054,1052,1,0,0,0,1054,1055,1,0,0,0,1055,1057, + 1,0,0,0,1056,1047,1,0,0,0,1056,1057,1,0,0,0,1057,226,1,0,0,0,1058, + 1062,7,4,0,0,1059,1061,7,5,0,0,1060,1059,1,0,0,0,1061,1064,1,0,0, + 0,1062,1060,1,0,0,0,1062,1063,1,0,0,0,1063,228,1,0,0,0,1064,1062, + 1,0,0,0,1065,1070,5,34,0,0,1066,1069,3,231,115,0,1067,1069,8,6,0, + 0,1068,1066,1,0,0,0,1068,1067,1,0,0,0,1069,1072,1,0,0,0,1070,1068, + 1,0,0,0,1070,1071,1,0,0,0,1071,1073,1,0,0,0,1072,1070,1,0,0,0,1073, + 1074,5,34,0,0,1074,230,1,0,0,0,1075,1083,5,92,0,0,1076,1084,7,7, + 0,0,1077,1078,5,117,0,0,1078,1079,3,233,116,0,1079,1080,3,233,116, + 0,1080,1081,3,233,116,0,1081,1082,3,233,116,0,1082,1084,1,0,0,0, + 1083,1076,1,0,0,0,1083,1077,1,0,0,0,1084,232,1,0,0,0,1085,1086,7, + 8,0,0,1086,234,1,0,0,0,1087,1088,5,47,0,0,1088,1089,5,47,0,0,1089, + 1093,1,0,0,0,1090,1092,8,9,0,0,1091,1090,1,0,0,0,1092,1095,1,0,0, + 0,1093,1091,1,0,0,0,1093,1094,1,0,0,0,1094,1096,1,0,0,0,1095,1093, + 1,0,0,0,1096,1097,6,117,0,0,1097,236,1,0,0,0,1098,1099,5,47,0,0, + 1099,1100,5,42,0,0,1100,1104,1,0,0,0,1101,1103,9,0,0,0,1102,1101, + 1,0,0,0,1103,1106,1,0,0,0,1104,1105,1,0,0,0,1104,1102,1,0,0,0,1105, + 1107,1,0,0,0,1106,1104,1,0,0,0,1107,1108,5,42,0,0,1108,1109,5,47, + 0,0,1109,1110,1,0,0,0,1110,1111,6,118,0,0,1111,238,1,0,0,0,1112, + 1114,7,10,0,0,1113,1112,1,0,0,0,1114,1115,1,0,0,0,1115,1113,1,0, + 0,0,1115,1116,1,0,0,0,1116,1117,1,0,0,0,1117,1118,6,119,0,0,1118, + 240,1,0,0,0,18,0,1019,1024,1027,1034,1037,1043,1045,1049,1054,1056, + 1062,1068,1070,1083,1093,1104,1115,1,0,1,0 ]; private static __ATN: antlr.ATN; diff --git a/src/capability-language/generated/QuixosCapabilityParser.ts b/src/capability-language/generated/QuixosCapabilityParser.ts index 2ab5291..5fcc69b 100644 --- a/src/capability-language/generated/QuixosCapabilityParser.ts +++ b/src/capability-language/generated/QuixosCapabilityParser.ts @@ -33,100 +33,101 @@ export class QuixosCapabilityParser extends antlr.Parser { public static readonly CONFORM = 21; public static readonly AS = 22; public static readonly BIND = 23; - public static readonly TO = 24; - public static readonly PRIVATE = 25; - public static readonly SHARED = 26; - public static readonly STATE = 27; - public static readonly EDGE = 28; - public static readonly PROJECTION = 29; - public static readonly WITH = 30; - public static readonly USING = 31; - public static readonly VIA = 32; - public static readonly MATERIALIZE = 33; - public static readonly IF = 34; - public static readonly ABSENT = 35; - public static readonly ON = 36; - public static readonly POLICY = 37; - public static readonly DEFAULT = 38; - public static readonly SOURCE = 39; - public static readonly REPOSITORY = 40; - public static readonly COMMIT = 41; - public static readonly REVISION = 42; - public static readonly SEMANTIC_MAJOR = 43; - public static readonly ON_DELETE = 44; - public static readonly RETAIN_OTHER = 45; - public static readonly KEYED = 46; - public static readonly PUBLIC_TRAVERSAL = 47; - public static readonly ID = 48; - public static readonly DOC = 49; - public static readonly MODE = 50; - public static readonly EMITS = 51; - public static readonly RECEIVER = 52; - public static readonly REQUIRES = 53; - public static readonly ANY = 54; - public static readonly GET = 55; - public static readonly SET = 56; - public static readonly WATCH = 57; - public static readonly START = 58; - public static readonly STOP = 59; - public static readonly READ = 60; - public static readonly WRITE = 61; - public static readonly RESOLVE = 62; - public static readonly CONNECT = 63; - public static readonly DISCONNECT = 64; - public static readonly CALL = 65; - public static readonly WATCH_START = 66; - public static readonly WATCH_STOP = 67; - public static readonly SUBSCRIBE = 68; - public static readonly UNSUBSCRIBE = 69; - public static readonly OPTIMISTIC_REGISTER = 70; - public static readonly CRDT = 71; - public static readonly OPTIONAL_ONE = 72; - public static readonly EXACTLY_ONE = 73; - public static readonly MANY_UNIQUE = 74; - public static readonly MANY = 75; - public static readonly ORDERED = 76; - public static readonly UNIT = 77; - public static readonly WATCH_HANDLE = 78; - public static readonly MESSAGE = 79; - public static readonly ATOM_REF = 80; - public static readonly INTERFACE_REF = 81; - public static readonly OPTIONAL = 82; - public static readonly LIST = 83; - public static readonly RECORD = 84; - public static readonly BOOL = 85; - public static readonly BYTES = 86; - public static readonly DOUBLE = 87; - public static readonly INT32 = 88; - public static readonly INT64 = 89; - public static readonly STRING = 90; - public static readonly UINT32 = 91; - public static readonly UINT64 = 92; - public static readonly TRUE = 93; - public static readonly FALSE = 94; - public static readonly NULL = 95; - public static readonly ARROW = 96; - public static readonly COLON = 97; - public static readonly SEMI = 98; - public static readonly COMMA = 99; - public static readonly DOT = 100; - public static readonly LBRACE = 101; - public static readonly RBRACE = 102; - public static readonly LBRACK = 103; - public static readonly RBRACK = 104; - public static readonly LPAREN = 105; - public static readonly RPAREN = 106; - public static readonly LT = 107; - public static readonly GT = 108; - public static readonly AMP = 109; - public static readonly EQUAL = 110; - public static readonly INTEGER = 111; - public static readonly JSON_NUMBER = 112; - public static readonly IDENTIFIER = 113; - public static readonly STRING_LITERAL = 114; - public static readonly LINE_COMMENT = 115; - public static readonly BLOCK_COMMENT = 116; - public static readonly WS = 117; + public static readonly STATIC = 24; + public static readonly TO = 25; + public static readonly PRIVATE = 26; + public static readonly SHARED = 27; + public static readonly STATE = 28; + public static readonly EDGE = 29; + public static readonly PROJECTION = 30; + public static readonly WITH = 31; + public static readonly USING = 32; + public static readonly VIA = 33; + public static readonly MATERIALIZE = 34; + public static readonly IF = 35; + public static readonly ABSENT = 36; + public static readonly ON = 37; + public static readonly POLICY = 38; + public static readonly DEFAULT = 39; + public static readonly SOURCE = 40; + public static readonly REPOSITORY = 41; + public static readonly COMMIT = 42; + public static readonly REVISION = 43; + public static readonly SEMANTIC_MAJOR = 44; + public static readonly ON_DELETE = 45; + public static readonly RETAIN_OTHER = 46; + public static readonly KEYED = 47; + public static readonly PUBLIC_TRAVERSAL = 48; + public static readonly ID = 49; + public static readonly DOC = 50; + public static readonly MODE = 51; + public static readonly EMITS = 52; + public static readonly RECEIVER = 53; + public static readonly REQUIRES = 54; + public static readonly ANY = 55; + public static readonly GET = 56; + public static readonly SET = 57; + public static readonly WATCH = 58; + public static readonly START = 59; + public static readonly STOP = 60; + public static readonly READ = 61; + public static readonly WRITE = 62; + public static readonly RESOLVE = 63; + public static readonly CONNECT = 64; + public static readonly DISCONNECT = 65; + public static readonly CALL = 66; + public static readonly WATCH_START = 67; + public static readonly WATCH_STOP = 68; + public static readonly SUBSCRIBE = 69; + public static readonly UNSUBSCRIBE = 70; + public static readonly OPTIMISTIC_REGISTER = 71; + public static readonly CRDT = 72; + public static readonly OPTIONAL_ONE = 73; + public static readonly EXACTLY_ONE = 74; + public static readonly MANY_UNIQUE = 75; + public static readonly MANY = 76; + public static readonly ORDERED = 77; + public static readonly UNIT = 78; + public static readonly WATCH_HANDLE = 79; + public static readonly MESSAGE = 80; + public static readonly ATOM_REF = 81; + public static readonly INTERFACE_REF = 82; + public static readonly OPTIONAL = 83; + public static readonly LIST = 84; + public static readonly RECORD = 85; + public static readonly BOOL = 86; + public static readonly BYTES = 87; + public static readonly DOUBLE = 88; + public static readonly INT32 = 89; + public static readonly INT64 = 90; + public static readonly STRING = 91; + public static readonly UINT32 = 92; + public static readonly UINT64 = 93; + public static readonly TRUE = 94; + public static readonly FALSE = 95; + public static readonly NULL = 96; + public static readonly ARROW = 97; + public static readonly COLON = 98; + public static readonly SEMI = 99; + public static readonly COMMA = 100; + public static readonly DOT = 101; + public static readonly LBRACE = 102; + public static readonly RBRACE = 103; + public static readonly LBRACK = 104; + public static readonly RBRACK = 105; + public static readonly LPAREN = 106; + public static readonly RPAREN = 107; + public static readonly LT = 108; + public static readonly GT = 109; + public static readonly AMP = 110; + public static readonly EQUAL = 111; + public static readonly INTEGER = 112; + public static readonly JSON_NUMBER = 113; + public static readonly IDENTIFIER = 114; + public static readonly STRING_LITERAL = 115; + public static readonly LINE_COMMENT = 116; + public static readonly BLOCK_COMMENT = 117; + public static readonly WS = 118; public static readonly RULE_document = 0; public static readonly RULE_fragmentDecl = 1; public static readonly RULE_sourceImportDecl = 2; @@ -172,43 +173,44 @@ export class QuixosCapabilityParser extends antlr.Parser { public static readonly RULE_edgeEndpoint = 42; public static readonly RULE_conformanceDecl = 43; public static readonly RULE_conformanceItem = 44; - public static readonly RULE_relationshipMaterializationDecl = 45; - public static readonly RULE_operationBindingDecl = 46; - public static readonly RULE_memberOperationRef = 47; - public static readonly RULE_operationName = 48; - public static readonly RULE_operationProvider = 49; - public static readonly RULE_statePrimitive = 50; - public static readonly RULE_edgePrimitive = 51; - public static readonly RULE_dependencyBindingBlock = 52; - public static readonly RULE_dependencyBinding = 53; - public static readonly RULE_constructorBindingDecl = 54; - public static readonly RULE_valueType = 55; - public static readonly RULE_recordField = 56; - public static readonly RULE_scalarType = 57; - public static readonly RULE_cardinality = 58; - public static readonly RULE_jsonLiteral = 59; - public static readonly RULE_jsonObject = 60; - public static readonly RULE_jsonMember = 61; - public static readonly RULE_jsonArray = 62; - public static readonly RULE_identifier = 63; - public static readonly RULE_stringLiteral = 64; + public static readonly RULE_stateFieldBindingDecl = 45; + public static readonly RULE_relationshipMaterializationDecl = 46; + public static readonly RULE_operationBindingDecl = 47; + public static readonly RULE_memberOperationRef = 48; + public static readonly RULE_operationName = 49; + public static readonly RULE_operationProvider = 50; + public static readonly RULE_statePrimitive = 51; + public static readonly RULE_edgePrimitive = 52; + public static readonly RULE_dependencyBindingBlock = 53; + public static readonly RULE_dependencyBinding = 54; + public static readonly RULE_constructorBindingDecl = 55; + public static readonly RULE_valueType = 56; + public static readonly RULE_recordField = 57; + public static readonly RULE_scalarType = 58; + public static readonly RULE_cardinality = 59; + public static readonly RULE_jsonLiteral = 60; + public static readonly RULE_jsonObject = 61; + public static readonly RULE_jsonMember = 62; + public static readonly RULE_jsonArray = 63; + public static readonly RULE_identifier = 64; + public static readonly RULE_stringLiteral = 65; public static readonly literalNames = [ null, "'workspace'", "'type'", "'object'", "'storable'", "'implements'", "'ref'", "'fragment'", "'import'", "'external'", "'atom'", "'interface'", "'interfaces'", "'package'", "'value'", "'relation'", "'operation'", "'function'", "'constructor'", "'constructs'", "'input'", "'conform'", - "'as'", "'bind'", "'to'", "'private'", "'shared'", "'state'", "'edge'", - "'projection'", "'with'", "'using'", "'via'", "'materialize'", "'if'", - "'absent'", "'on'", "'policy'", "'default'", "'source'", "'repository'", - "'commit'", "'revision'", "'semantic-major'", "'on-delete'", "'retain-other'", - "'keyed'", "'public-traversal'", "'id'", "'doc'", "'mode'", "'emits'", - "'receiver'", "'requires'", "'any'", "'get'", "'set'", "'watch'", - "'start'", "'stop'", "'read'", "'write'", "'resolve'", "'connect'", - "'disconnect'", "'call'", "'watch-start'", "'watch-stop'", "'subscribe'", - "'unsubscribe'", "'optimistic-register'", "'crdt'", "'optional-one'", - "'exactly-one'", "'many-unique'", "'many'", "'ordered'", "'unit'", - "'watch-handle'", "'message'", "'atom-ref'", "'interface-ref'", + "'as'", "'bind'", "'static'", "'to'", "'private'", "'shared'", "'state'", + "'edge'", "'projection'", "'with'", "'using'", "'via'", "'materialize'", + "'if'", "'absent'", "'on'", "'policy'", "'default'", "'source'", + "'repository'", "'commit'", "'revision'", "'semantic-major'", "'on-delete'", + "'retain-other'", "'keyed'", "'public-traversal'", "'id'", "'doc'", + "'mode'", "'emits'", "'receiver'", "'requires'", "'any'", "'get'", + "'set'", "'watch'", "'start'", "'stop'", "'read'", "'write'", "'resolve'", + "'connect'", "'disconnect'", "'call'", "'watch-start'", "'watch-stop'", + "'subscribe'", "'unsubscribe'", "'optimistic-register'", "'crdt'", + "'optional-one'", "'exactly-one'", "'many-unique'", "'many'", "'ordered'", + "'unit'", "'watch-handle'", "'message'", "'atom-ref'", "'interface-ref'", "'optional'", "'list'", "'record'", "'bool'", "'bytes'", "'double'", "'int32'", "'int64'", "'string'", "'uint32'", "'uint64'", "'true'", "'false'", "'null'", "'->'", "':'", "';'", "','", "'.'", "'{'", @@ -219,22 +221,22 @@ export class QuixosCapabilityParser extends antlr.Parser { null, "WORKSPACE", "TYPE", "OBJECT", "STORABLE", "IMPLEMENTS", "REF", "FRAGMENT", "IMPORT", "EXTERNAL", "ATOM", "INTERFACE", "INTERFACES", "PACKAGE", "VALUE", "RELATION", "OPERATION", "FUNCTION", "CONSTRUCTOR", - "CONSTRUCTS", "INPUT", "CONFORM", "AS", "BIND", "TO", "PRIVATE", - "SHARED", "STATE", "EDGE", "PROJECTION", "WITH", "USING", "VIA", - "MATERIALIZE", "IF", "ABSENT", "ON", "POLICY", "DEFAULT", "SOURCE", - "REPOSITORY", "COMMIT", "REVISION", "SEMANTIC_MAJOR", "ON_DELETE", - "RETAIN_OTHER", "KEYED", "PUBLIC_TRAVERSAL", "ID", "DOC", "MODE", - "EMITS", "RECEIVER", "REQUIRES", "ANY", "GET", "SET", "WATCH", "START", - "STOP", "READ", "WRITE", "RESOLVE", "CONNECT", "DISCONNECT", "CALL", - "WATCH_START", "WATCH_STOP", "SUBSCRIBE", "UNSUBSCRIBE", "OPTIMISTIC_REGISTER", - "CRDT", "OPTIONAL_ONE", "EXACTLY_ONE", "MANY_UNIQUE", "MANY", "ORDERED", - "UNIT", "WATCH_HANDLE", "MESSAGE", "ATOM_REF", "INTERFACE_REF", - "OPTIONAL", "LIST", "RECORD", "BOOL", "BYTES", "DOUBLE", "INT32", - "INT64", "STRING", "UINT32", "UINT64", "TRUE", "FALSE", "NULL", - "ARROW", "COLON", "SEMI", "COMMA", "DOT", "LBRACE", "RBRACE", "LBRACK", - "RBRACK", "LPAREN", "RPAREN", "LT", "GT", "AMP", "EQUAL", "INTEGER", - "JSON_NUMBER", "IDENTIFIER", "STRING_LITERAL", "LINE_COMMENT", "BLOCK_COMMENT", - "WS" + "CONSTRUCTS", "INPUT", "CONFORM", "AS", "BIND", "STATIC", "TO", + "PRIVATE", "SHARED", "STATE", "EDGE", "PROJECTION", "WITH", "USING", + "VIA", "MATERIALIZE", "IF", "ABSENT", "ON", "POLICY", "DEFAULT", + "SOURCE", "REPOSITORY", "COMMIT", "REVISION", "SEMANTIC_MAJOR", + "ON_DELETE", "RETAIN_OTHER", "KEYED", "PUBLIC_TRAVERSAL", "ID", + "DOC", "MODE", "EMITS", "RECEIVER", "REQUIRES", "ANY", "GET", "SET", + "WATCH", "START", "STOP", "READ", "WRITE", "RESOLVE", "CONNECT", + "DISCONNECT", "CALL", "WATCH_START", "WATCH_STOP", "SUBSCRIBE", + "UNSUBSCRIBE", "OPTIMISTIC_REGISTER", "CRDT", "OPTIONAL_ONE", "EXACTLY_ONE", + "MANY_UNIQUE", "MANY", "ORDERED", "UNIT", "WATCH_HANDLE", "MESSAGE", + "ATOM_REF", "INTERFACE_REF", "OPTIONAL", "LIST", "RECORD", "BOOL", + "BYTES", "DOUBLE", "INT32", "INT64", "STRING", "UINT32", "UINT64", + "TRUE", "FALSE", "NULL", "ARROW", "COLON", "SEMI", "COMMA", "DOT", + "LBRACE", "RBRACE", "LBRACK", "RBRACK", "LPAREN", "RPAREN", "LT", + "GT", "AMP", "EQUAL", "INTEGER", "JSON_NUMBER", "IDENTIFIER", "STRING_LITERAL", + "LINE_COMMENT", "BLOCK_COMMENT", "WS" ]; public static readonly ruleNames = [ "document", "fragmentDecl", "sourceImportDecl", "workspaceDecl", @@ -248,7 +250,7 @@ export class QuixosCapabilityParser extends antlr.Parser { "operationMode", "receiverRequirement", "identifierList", "dependencyBlock", "dependencyPort", "primitiveList", "primitive", "sharedAttachmentDecl", "attachmentDecl", "stateDecl", "storagePolicy", "edgeDecl", "edgeEndpoint", - "conformanceDecl", "conformanceItem", "relationshipMaterializationDecl", + "conformanceDecl", "conformanceItem", "stateFieldBindingDecl", "relationshipMaterializationDecl", "operationBindingDecl", "memberOperationRef", "operationName", "operationProvider", "statePrimitive", "edgePrimitive", "dependencyBindingBlock", "dependencyBinding", "constructorBindingDecl", "valueType", "recordField", "scalarType", @@ -274,42 +276,42 @@ export class QuixosCapabilityParser extends antlr.Parser { let localContext = new DocumentContext(this.context, this.state); this.enterRule(localContext, 0, QuixosCapabilityParser.RULE_document); try { - this.state = 142; + this.state = 144; this.errorHandler.sync(this); switch (this.interpreter.adaptivePredict(this.tokenStream, 0, this.context) ) { case 1: this.enterOuterAlt(localContext, 1); { - this.state = 130; + this.state = 132; this.workspaceDecl(); - this.state = 131; + this.state = 133; this.match(QuixosCapabilityParser.EOF); } break; case 2: this.enterOuterAlt(localContext, 2); { - this.state = 133; + this.state = 135; this.interfaceResourceDecl(); - this.state = 134; + this.state = 136; this.match(QuixosCapabilityParser.EOF); } break; case 3: this.enterOuterAlt(localContext, 3); { - this.state = 136; + this.state = 138; this.packageResourceDecl(); - this.state = 137; + this.state = 139; this.match(QuixosCapabilityParser.EOF); } break; case 4: this.enterOuterAlt(localContext, 4); { - this.state = 139; + this.state = 141; this.fragmentDecl(); - this.state = 140; + this.state = 142; this.match(QuixosCapabilityParser.EOF); } break; @@ -335,25 +337,25 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 144; + this.state = 146; this.match(QuixosCapabilityParser.FRAGMENT); - this.state = 145; + this.state = 147; this.match(QuixosCapabilityParser.LBRACE); - this.state = 149; + this.state = 151; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 69469444) !== 0)) { + while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 136578308) !== 0)) { { { - this.state = 146; + this.state = 148; this.workspaceItem(); } } - this.state = 151; + this.state = 153; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 152; + this.state = 154; this.match(QuixosCapabilityParser.RBRACE); } } @@ -376,11 +378,11 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 154; - this.match(QuixosCapabilityParser.IMPORT); - this.state = 155; - this.stringLiteral(); this.state = 156; + this.match(QuixosCapabilityParser.IMPORT); + this.state = 157; + this.stringLiteral(); + this.state = 158; this.match(QuixosCapabilityParser.SEMI); } } @@ -404,39 +406,39 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 158; - this.match(QuixosCapabilityParser.WORKSPACE); - this.state = 159; - this.identifier(); this.state = 160; - this.match(QuixosCapabilityParser.ID); + this.match(QuixosCapabilityParser.WORKSPACE); this.state = 161; - this.stringLiteral(); + this.identifier(); this.state = 162; - this.match(QuixosCapabilityParser.REVISION); + this.match(QuixosCapabilityParser.ID); this.state = 163; this.stringLiteral(); this.state = 164; - this.match(QuixosCapabilityParser.COMMIT); + this.match(QuixosCapabilityParser.REVISION); this.state = 165; this.stringLiteral(); this.state = 166; + this.match(QuixosCapabilityParser.COMMIT); + this.state = 167; + this.stringLiteral(); + this.state = 168; this.match(QuixosCapabilityParser.LBRACE); - this.state = 170; + this.state = 172; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 69469444) !== 0)) { + while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 136578308) !== 0)) { { { - this.state = 167; + this.state = 169; this.workspaceItem(); } } - this.state = 172; + this.state = 174; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 173; + this.state = 175; this.match(QuixosCapabilityParser.RBRACE); } } @@ -457,55 +459,55 @@ export class QuixosCapabilityParser extends antlr.Parser { let localContext = new WorkspaceItemContext(this.context, this.state); this.enterRule(localContext, 8, QuixosCapabilityParser.RULE_workspaceItem); try { - this.state = 182; + this.state = 184; this.errorHandler.sync(this); switch (this.interpreter.adaptivePredict(this.tokenStream, 3, this.context) ) { case 1: this.enterOuterAlt(localContext, 1); { - this.state = 175; + this.state = 177; this.sourceImportDecl(); } break; case 2: this.enterOuterAlt(localContext, 2); { - this.state = 176; + this.state = 178; this.atomDecl(); } break; case 3: this.enterOuterAlt(localContext, 3); { - this.state = 177; + this.state = 179; this.resourceImportDecl(); } break; case 4: this.enterOuterAlt(localContext, 4); { - this.state = 178; + this.state = 180; this.sharedAttachmentDecl(); } break; case 5: this.enterOuterAlt(localContext, 5); { - this.state = 179; + this.state = 181; this.conformanceDecl(); } break; case 6: this.enterOuterAlt(localContext, 6); { - this.state = 180; + this.state = 182; this.constructorBindingDecl(); } break; case 7: this.enterOuterAlt(localContext, 7); { - this.state = 181; + this.state = 183; this.typeAliasDecl(); } break; @@ -528,32 +530,32 @@ export class QuixosCapabilityParser extends antlr.Parser { let localContext = new ResourceImportDeclContext(this.context, this.state); this.enterRule(localContext, 10, QuixosCapabilityParser.RULE_resourceImportDecl); try { - this.state = 194; + this.state = 196; this.errorHandler.sync(this); switch (this.interpreter.adaptivePredict(this.tokenStream, 4, this.context) ) { case 1: this.enterOuterAlt(localContext, 1); { - this.state = 184; - this.match(QuixosCapabilityParser.IMPORT); - this.state = 185; - this.match(QuixosCapabilityParser.INTERFACE); this.state = 186; - this.identifier(); + this.match(QuixosCapabilityParser.IMPORT); this.state = 187; + this.match(QuixosCapabilityParser.INTERFACE); + this.state = 188; + this.identifier(); + this.state = 189; this.match(QuixosCapabilityParser.SEMI); } break; case 2: this.enterOuterAlt(localContext, 2); { - this.state = 189; - this.match(QuixosCapabilityParser.IMPORT); - this.state = 190; - this.match(QuixosCapabilityParser.PACKAGE); this.state = 191; - this.identifier(); + this.match(QuixosCapabilityParser.IMPORT); this.state = 192; + this.match(QuixosCapabilityParser.PACKAGE); + this.state = 193; + this.identifier(); + this.state = 194; this.match(QuixosCapabilityParser.SEMI); } break; @@ -578,17 +580,17 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 196; - this.match(QuixosCapabilityParser.EXTERNAL); - this.state = 197; - this.match(QuixosCapabilityParser.ATOM); this.state = 198; - this.identifier(); + this.match(QuixosCapabilityParser.EXTERNAL); this.state = 199; - this.match(QuixosCapabilityParser.ID); + this.match(QuixosCapabilityParser.ATOM); this.state = 200; - this.stringLiteral(); + this.identifier(); this.state = 201; + this.match(QuixosCapabilityParser.ID); + this.state = 202; + this.stringLiteral(); + this.state = 203; this.match(QuixosCapabilityParser.SEMI); } } @@ -611,17 +613,17 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 203; - this.match(QuixosCapabilityParser.EXTERNAL); - this.state = 204; - this.match(QuixosCapabilityParser.INTERFACE); this.state = 205; - this.identifier(); + this.match(QuixosCapabilityParser.EXTERNAL); this.state = 206; - this.match(QuixosCapabilityParser.REVISION); + this.match(QuixosCapabilityParser.INTERFACE); this.state = 207; - this.stringLiteral(); + this.identifier(); this.state = 208; + this.match(QuixosCapabilityParser.REVISION); + this.state = 209; + this.stringLiteral(); + this.state = 210; this.match(QuixosCapabilityParser.SEMI); } } @@ -642,34 +644,34 @@ export class QuixosCapabilityParser extends antlr.Parser { let localContext = new ResourcePreambleContext(this.context, this.state); this.enterRule(localContext, 16, QuixosCapabilityParser.RULE_resourcePreamble); try { - this.state = 214; + this.state = 216; this.errorHandler.sync(this); switch (this.interpreter.adaptivePredict(this.tokenStream, 5, this.context) ) { case 1: this.enterOuterAlt(localContext, 1); { - this.state = 210; + this.state = 212; this.resourceImportDecl(); } break; case 2: this.enterOuterAlt(localContext, 2); { - this.state = 211; + this.state = 213; this.externalAtomDecl(); } break; case 3: this.enterOuterAlt(localContext, 3); { - this.state = 212; + this.state = 214; this.externalInterfaceDecl(); } break; case 4: this.enterOuterAlt(localContext, 4); { - this.state = 213; + this.state = 215; this.typeAliasDecl(); } break; @@ -695,27 +697,27 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 216; - this.match(QuixosCapabilityParser.ATOM); - this.state = 217; - this.identifier(); this.state = 218; - this.match(QuixosCapabilityParser.ID); + this.match(QuixosCapabilityParser.ATOM); this.state = 219; + this.identifier(); + this.state = 220; + this.match(QuixosCapabilityParser.ID); + this.state = 221; this.stringLiteral(); - this.state = 222; + this.state = 224; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 49) { + if (_la === 50) { { - this.state = 220; + this.state = 222; this.match(QuixosCapabilityParser.DOC); - this.state = 221; + this.state = 223; this.stringLiteral(); } } - this.state = 224; + this.state = 226; this.match(QuixosCapabilityParser.SEMI); } } @@ -739,87 +741,87 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 229; + this.state = 231; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 772) !== 0)) { { { - this.state = 226; + this.state = 228; this.resourcePreamble(); } } - this.state = 231; + this.state = 233; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 232; + this.state = 234; this.match(QuixosCapabilityParser.INTERFACE); - this.state = 233; - this.identifier(); this.state = 235; + this.identifier(); + this.state = 237; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 107) { + if (_la === 108) { { - this.state = 234; + this.state = 236; this.typeParameters(); } } - this.state = 237; - this.match(QuixosCapabilityParser.ID); - this.state = 238; - this.stringLiteral(); this.state = 239; - this.match(QuixosCapabilityParser.REVISION); + this.match(QuixosCapabilityParser.ID); this.state = 240; this.stringLiteral(); - this.state = 250; + this.state = 241; + this.match(QuixosCapabilityParser.REVISION); + this.state = 242; + this.stringLiteral(); + this.state = 252; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 53) { + if (_la === 54) { { - this.state = 241; + this.state = 243; this.match(QuixosCapabilityParser.REQUIRES); - this.state = 242; + this.state = 244; this.interfaceType(); - this.state = 247; + this.state = 249; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (_la === 99) { + while (_la === 100) { { { - this.state = 243; + this.state = 245; this.match(QuixosCapabilityParser.COMMA); - this.state = 244; + this.state = 246; this.interfaceType(); } } - this.state = 249; + this.state = 251; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } } } - this.state = 252; + this.state = 254; this.match(QuixosCapabilityParser.LBRACE); - this.state = 256; + this.state = 258; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 114688) !== 0)) { + while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 16891904) !== 0)) { { { - this.state = 253; + this.state = 255; this.interfaceMember(); } } - this.state = 258; + this.state = 260; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 259; + this.state = 261; this.match(QuixosCapabilityParser.RBRACE); } } @@ -843,27 +845,27 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 261; + this.state = 263; this.match(QuixosCapabilityParser.LT); - this.state = 262; + this.state = 264; this.typeParameter(); - this.state = 267; + this.state = 269; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (_la === 99) { + while (_la === 100) { { { - this.state = 263; + this.state = 265; this.match(QuixosCapabilityParser.COMMA); - this.state = 264; + this.state = 266; this.typeParameter(); } } - this.state = 269; + this.state = 271; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 270; + this.state = 272; this.match(QuixosCapabilityParser.GT); } } @@ -885,24 +887,24 @@ export class QuixosCapabilityParser extends antlr.Parser { this.enterRule(localContext, 24, QuixosCapabilityParser.RULE_typeParameter); let _la: number; try { - this.state = 291; + this.state = 293; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.VALUE: this.enterOuterAlt(localContext, 1); { - this.state = 272; + this.state = 274; this.match(QuixosCapabilityParser.VALUE); - this.state = 273; + this.state = 275; this.identifier(); - this.state = 276; + this.state = 278; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 97) { + if (_la === 98) { { - this.state = 274; + this.state = 276; this.match(QuixosCapabilityParser.COLON); - this.state = 275; + this.state = 277; this.match(QuixosCapabilityParser.STORABLE); } } @@ -912,32 +914,32 @@ export class QuixosCapabilityParser extends antlr.Parser { case QuixosCapabilityParser.OBJECT: this.enterOuterAlt(localContext, 2); { - this.state = 278; + this.state = 280; this.match(QuixosCapabilityParser.OBJECT); - this.state = 279; + this.state = 281; this.identifier(); - this.state = 289; + this.state = 291; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); if (_la === 5) { { - this.state = 280; + this.state = 282; this.match(QuixosCapabilityParser.IMPLEMENTS); - this.state = 281; + this.state = 283; this.interfaceType(); - this.state = 286; + this.state = 288; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (_la === 109) { + while (_la === 110) { { { - this.state = 282; + this.state = 284; this.match(QuixosCapabilityParser.AMP); - this.state = 283; + this.state = 285; this.interfaceType(); } } - this.state = 288; + this.state = 290; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } @@ -970,14 +972,14 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 293; - this.identifier(); this.state = 295; + this.identifier(); + this.state = 297; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 107) { + if (_la === 108) { { - this.state = 294; + this.state = 296; this.typeArguments(); } } @@ -1004,27 +1006,27 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 297; + this.state = 299; this.match(QuixosCapabilityParser.LT); - this.state = 298; + this.state = 300; this.typeArgument(); - this.state = 303; + this.state = 305; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (_la === 99) { + while (_la === 100) { { { - this.state = 299; + this.state = 301; this.match(QuixosCapabilityParser.COMMA); - this.state = 300; + this.state = 302; this.typeArgument(); } } - this.state = 305; + this.state = 307; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 306; + this.state = 308; this.match(QuixosCapabilityParser.GT); } } @@ -1045,33 +1047,33 @@ export class QuixosCapabilityParser extends antlr.Parser { let localContext = new TypeArgumentContext(this.context, this.state); this.enterRule(localContext, 30, QuixosCapabilityParser.RULE_typeArgument); try { - this.state = 315; + this.state = 317; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.ATOM: this.enterOuterAlt(localContext, 1); { - this.state = 308; + this.state = 310; this.match(QuixosCapabilityParser.ATOM); - this.state = 309; + this.state = 311; this.identifier(); } break; case QuixosCapabilityParser.INTERFACE: this.enterOuterAlt(localContext, 2); { - this.state = 310; + this.state = 312; this.match(QuixosCapabilityParser.INTERFACE); - this.state = 311; + this.state = 313; this.interfaceType(); } break; case QuixosCapabilityParser.OBJECT: this.enterOuterAlt(localContext, 3); { - this.state = 312; + this.state = 314; this.match(QuixosCapabilityParser.OBJECT); - this.state = 313; + this.state = 315; this.identifier(); } break; @@ -1096,7 +1098,7 @@ export class QuixosCapabilityParser extends antlr.Parser { case QuixosCapabilityParser.IDENTIFIER: this.enterOuterAlt(localContext, 4); { - this.state = 314; + this.state = 316; this.valueType(); } break; @@ -1124,25 +1126,25 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 317; + this.state = 319; this.match(QuixosCapabilityParser.TYPE); - this.state = 318; - this.identifier(); this.state = 320; + this.identifier(); + this.state = 322; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 107) { + if (_la === 108) { { - this.state = 319; + this.state = 321; this.typeParameters(); } } - this.state = 322; - this.match(QuixosCapabilityParser.EQUAL); - this.state = 323; - this.valueType(); this.state = 324; + this.match(QuixosCapabilityParser.EQUAL); + this.state = 325; + this.valueType(); + this.state = 326; this.match(QuixosCapabilityParser.SEMI); } } @@ -1163,27 +1165,28 @@ export class QuixosCapabilityParser extends antlr.Parser { let localContext = new InterfaceMemberContext(this.context, this.state); this.enterRule(localContext, 34, QuixosCapabilityParser.RULE_interfaceMember); try { - this.state = 329; + this.state = 331; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.VALUE: this.enterOuterAlt(localContext, 1); { - this.state = 326; + this.state = 328; this.valueMember(); } break; case QuixosCapabilityParser.RELATION: this.enterOuterAlt(localContext, 2); { - this.state = 327; + this.state = 329; this.relationshipMember(); } break; case QuixosCapabilityParser.OPERATION: + case QuixosCapabilityParser.STATIC: this.enterOuterAlt(localContext, 3); { - this.state = 328; + this.state = 330; this.operationMember(); } break; @@ -1207,36 +1210,47 @@ export class QuixosCapabilityParser extends antlr.Parser { public operationMember(): OperationMemberContext { let localContext = new OperationMemberContext(this.context, this.state); this.enterRule(localContext, 36, QuixosCapabilityParser.RULE_operationMember); + let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 331; - this.match(QuixosCapabilityParser.OPERATION); - this.state = 332; - this.identifier(); - this.state = 333; - this.match(QuixosCapabilityParser.ID); this.state = 334; - this.stringLiteral(); - this.state = 335; - this.match(QuixosCapabilityParser.COLON); + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + if (_la === 24) { + { + this.state = 333; + this.match(QuixosCapabilityParser.STATIC); + } + } + this.state = 336; - this.valueType(); + this.match(QuixosCapabilityParser.OPERATION); this.state = 337; - this.match(QuixosCapabilityParser.ARROW); + this.identifier(); this.state = 338; - this.valueType(); - this.state = 339; - this.match(QuixosCapabilityParser.LBRACE); - this.state = 340; - this.match(QuixosCapabilityParser.CALL); - this.state = 341; this.match(QuixosCapabilityParser.ID); - this.state = 342; + this.state = 339; this.stringLiteral(); + this.state = 340; + this.match(QuixosCapabilityParser.COLON); + this.state = 341; + this.valueType(); + this.state = 342; + this.match(QuixosCapabilityParser.ARROW); this.state = 343; - this.match(QuixosCapabilityParser.SEMI); + this.valueType(); this.state = 344; + this.match(QuixosCapabilityParser.LBRACE); + this.state = 345; + this.match(QuixosCapabilityParser.CALL); + this.state = 346; + this.match(QuixosCapabilityParser.ID); + this.state = 347; + this.stringLiteral(); + this.state = 348; + this.match(QuixosCapabilityParser.SEMI); + this.state = 349; this.match(QuixosCapabilityParser.RBRACE); } } @@ -1260,35 +1274,35 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 346; - this.match(QuixosCapabilityParser.VALUE); - this.state = 347; - this.identifier(); - this.state = 348; - this.match(QuixosCapabilityParser.ID); - this.state = 349; - this.stringLiteral(); - this.state = 350; - this.match(QuixosCapabilityParser.COLON); this.state = 351; - this.valueType(); + this.match(QuixosCapabilityParser.VALUE); this.state = 352; - this.match(QuixosCapabilityParser.LBRACE); + this.identifier(); + this.state = 353; + this.match(QuixosCapabilityParser.ID); + this.state = 354; + this.stringLiteral(); + this.state = 355; + this.match(QuixosCapabilityParser.COLON); this.state = 356; + this.valueType(); + this.state = 357; + this.match(QuixosCapabilityParser.LBRACE); + this.state = 361; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (((((_la - 55)) & ~0x1F) === 0 && ((1 << (_la - 55)) & 7) !== 0)) { + while (((((_la - 56)) & ~0x1F) === 0 && ((1 << (_la - 56)) & 7) !== 0)) { { { - this.state = 353; + this.state = 358; this.valueMemberOperation(); } } - this.state = 358; + this.state = 363; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 359; + this.state = 364; this.match(QuixosCapabilityParser.RBRACE); } } @@ -1309,27 +1323,14 @@ export class QuixosCapabilityParser extends antlr.Parser { let localContext = new ValueMemberOperationContext(this.context, this.state); this.enterRule(localContext, 40, QuixosCapabilityParser.RULE_valueMemberOperation); try { - this.state = 380; + this.state = 385; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.GET: this.enterOuterAlt(localContext, 1); { - this.state = 361; - this.match(QuixosCapabilityParser.GET); - this.state = 362; - this.match(QuixosCapabilityParser.ID); - this.state = 363; - this.stringLiteral(); - this.state = 364; - this.match(QuixosCapabilityParser.SEMI); - } - break; - case QuixosCapabilityParser.SET: - this.enterOuterAlt(localContext, 2); - { this.state = 366; - this.match(QuixosCapabilityParser.SET); + this.match(QuixosCapabilityParser.GET); this.state = 367; this.match(QuixosCapabilityParser.ID); this.state = 368; @@ -1338,24 +1339,37 @@ export class QuixosCapabilityParser extends antlr.Parser { this.match(QuixosCapabilityParser.SEMI); } break; + case QuixosCapabilityParser.SET: + this.enterOuterAlt(localContext, 2); + { + this.state = 371; + this.match(QuixosCapabilityParser.SET); + this.state = 372; + this.match(QuixosCapabilityParser.ID); + this.state = 373; + this.stringLiteral(); + this.state = 374; + this.match(QuixosCapabilityParser.SEMI); + } + break; case QuixosCapabilityParser.WATCH: this.enterOuterAlt(localContext, 3); { - this.state = 371; - this.match(QuixosCapabilityParser.WATCH); - this.state = 372; - this.match(QuixosCapabilityParser.START); - this.state = 373; - this.match(QuixosCapabilityParser.ID); - this.state = 374; - this.stringLiteral(); - this.state = 375; - this.match(QuixosCapabilityParser.STOP); this.state = 376; - this.match(QuixosCapabilityParser.ID); + this.match(QuixosCapabilityParser.WATCH); this.state = 377; - this.stringLiteral(); + this.match(QuixosCapabilityParser.START); this.state = 378; + this.match(QuixosCapabilityParser.ID); + this.state = 379; + this.stringLiteral(); + this.state = 380; + this.match(QuixosCapabilityParser.STOP); + this.state = 381; + this.match(QuixosCapabilityParser.ID); + this.state = 382; + this.stringLiteral(); + this.state = 383; this.match(QuixosCapabilityParser.SEMI); } break; @@ -1383,47 +1397,47 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 382; - this.match(QuixosCapabilityParser.RELATION); - this.state = 383; - this.identifier(); - this.state = 384; - this.match(QuixosCapabilityParser.ID); - this.state = 385; - this.stringLiteral(); - this.state = 386; - this.match(QuixosCapabilityParser.COLON); this.state = 387; - this.cardinality(); + this.match(QuixosCapabilityParser.RELATION); this.state = 388; - this.targetConstraint(); + this.identifier(); + this.state = 389; + this.match(QuixosCapabilityParser.ID); this.state = 390; + this.stringLiteral(); + this.state = 391; + this.match(QuixosCapabilityParser.COLON); + this.state = 392; + this.cardinality(); + this.state = 393; + this.targetConstraint(); + this.state = 395; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 76) { + if (_la === 77) { { - this.state = 389; + this.state = 394; this.match(QuixosCapabilityParser.ORDERED); } } - this.state = 392; + this.state = 397; this.match(QuixosCapabilityParser.LBRACE); - this.state = 396; + this.state = 401; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (((((_la - 57)) & ~0x1F) === 0 && ((1 << (_la - 57)) & 225) !== 0)) { + while (((((_la - 58)) & ~0x1F) === 0 && ((1 << (_la - 58)) & 225) !== 0)) { { { - this.state = 393; + this.state = 398; this.relationshipOperation(); } } - this.state = 398; + this.state = 403; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 399; + this.state = 404; this.match(QuixosCapabilityParser.RBRACE); } } @@ -1444,27 +1458,14 @@ export class QuixosCapabilityParser extends antlr.Parser { let localContext = new RelationshipOperationContext(this.context, this.state); this.enterRule(localContext, 44, QuixosCapabilityParser.RULE_relationshipOperation); try { - this.state = 425; + this.state = 430; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.RESOLVE: this.enterOuterAlt(localContext, 1); { - this.state = 401; - this.match(QuixosCapabilityParser.RESOLVE); - this.state = 402; - this.match(QuixosCapabilityParser.ID); - this.state = 403; - this.stringLiteral(); - this.state = 404; - this.match(QuixosCapabilityParser.SEMI); - } - break; - case QuixosCapabilityParser.CONNECT: - this.enterOuterAlt(localContext, 2); - { this.state = 406; - this.match(QuixosCapabilityParser.CONNECT); + this.match(QuixosCapabilityParser.RESOLVE); this.state = 407; this.match(QuixosCapabilityParser.ID); this.state = 408; @@ -1473,11 +1474,11 @@ export class QuixosCapabilityParser extends antlr.Parser { this.match(QuixosCapabilityParser.SEMI); } break; - case QuixosCapabilityParser.DISCONNECT: - this.enterOuterAlt(localContext, 3); + case QuixosCapabilityParser.CONNECT: + this.enterOuterAlt(localContext, 2); { this.state = 411; - this.match(QuixosCapabilityParser.DISCONNECT); + this.match(QuixosCapabilityParser.CONNECT); this.state = 412; this.match(QuixosCapabilityParser.ID); this.state = 413; @@ -1486,24 +1487,37 @@ export class QuixosCapabilityParser extends antlr.Parser { this.match(QuixosCapabilityParser.SEMI); } break; + case QuixosCapabilityParser.DISCONNECT: + this.enterOuterAlt(localContext, 3); + { + this.state = 416; + this.match(QuixosCapabilityParser.DISCONNECT); + this.state = 417; + this.match(QuixosCapabilityParser.ID); + this.state = 418; + this.stringLiteral(); + this.state = 419; + this.match(QuixosCapabilityParser.SEMI); + } + break; case QuixosCapabilityParser.WATCH: this.enterOuterAlt(localContext, 4); { - this.state = 416; - this.match(QuixosCapabilityParser.WATCH); - this.state = 417; - this.match(QuixosCapabilityParser.START); - this.state = 418; - this.match(QuixosCapabilityParser.ID); - this.state = 419; - this.stringLiteral(); - this.state = 420; - this.match(QuixosCapabilityParser.STOP); this.state = 421; - this.match(QuixosCapabilityParser.ID); + this.match(QuixosCapabilityParser.WATCH); this.state = 422; - this.stringLiteral(); + this.match(QuixosCapabilityParser.START); this.state = 423; + this.match(QuixosCapabilityParser.ID); + this.state = 424; + this.stringLiteral(); + this.state = 425; + this.match(QuixosCapabilityParser.STOP); + this.state = 426; + this.match(QuixosCapabilityParser.ID); + this.state = 427; + this.stringLiteral(); + this.state = 428; this.match(QuixosCapabilityParser.SEMI); } break; @@ -1529,31 +1543,31 @@ export class QuixosCapabilityParser extends antlr.Parser { this.enterRule(localContext, 46, QuixosCapabilityParser.RULE_targetConstraint); let _la: number; try { - this.state = 436; + this.state = 441; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.ATOM: this.enterOuterAlt(localContext, 1); { - this.state = 427; + this.state = 432; this.match(QuixosCapabilityParser.ATOM); - this.state = 428; + this.state = 433; this.identifier(); } break; case QuixosCapabilityParser.INTERFACE: this.enterOuterAlt(localContext, 2); { - this.state = 429; + this.state = 434; this.match(QuixosCapabilityParser.INTERFACE); - this.state = 430; + this.state = 435; this.identifier(); - this.state = 432; + this.state = 437; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 107) { + if (_la === 108) { { - this.state = 431; + this.state = 436; this.typeArguments(); } } @@ -1563,9 +1577,9 @@ export class QuixosCapabilityParser extends antlr.Parser { case QuixosCapabilityParser.OBJECT: this.enterOuterAlt(localContext, 3); { - this.state = 434; + this.state = 439; this.match(QuixosCapabilityParser.OBJECT); - this.state = 435; + this.state = 440; this.identifier(); } break; @@ -1593,61 +1607,61 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 441; + this.state = 446; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 772) !== 0)) { { { - this.state = 438; + this.state = 443; this.resourcePreamble(); } } - this.state = 443; + this.state = 448; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 444; - this.match(QuixosCapabilityParser.PACKAGE); - this.state = 445; - this.identifier(); - this.state = 446; - this.match(QuixosCapabilityParser.ID); - this.state = 447; - this.stringLiteral(); - this.state = 448; - this.match(QuixosCapabilityParser.REVISION); this.state = 449; - this.stringLiteral(); + this.match(QuixosCapabilityParser.PACKAGE); + this.state = 450; + this.identifier(); + this.state = 451; + this.match(QuixosCapabilityParser.ID); this.state = 452; + this.stringLiteral(); + this.state = 453; + this.match(QuixosCapabilityParser.REVISION); + this.state = 454; + this.stringLiteral(); + this.state = 457; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 43) { + if (_la === 44) { { - this.state = 450; + this.state = 455; this.match(QuixosCapabilityParser.SEMANTIC_MAJOR); - this.state = 451; + this.state = 456; this.match(QuixosCapabilityParser.INTEGER); } } - this.state = 454; + this.state = 459; this.match(QuixosCapabilityParser.LBRACE); - this.state = 458; + this.state = 463; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 458752) !== 0)) { { { - this.state = 455; + this.state = 460; this.packageExport(); } } - this.state = 460; + this.state = 465; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 461; + this.state = 466; this.match(QuixosCapabilityParser.RBRACE); } } @@ -1668,27 +1682,27 @@ export class QuixosCapabilityParser extends antlr.Parser { let localContext = new PackageExportContext(this.context, this.state); this.enterRule(localContext, 50, QuixosCapabilityParser.RULE_packageExport); try { - this.state = 466; + this.state = 471; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.OPERATION: this.enterOuterAlt(localContext, 1); { - this.state = 463; + this.state = 468; this.packageOperationExport(); } break; case QuixosCapabilityParser.FUNCTION: this.enterOuterAlt(localContext, 2); { - this.state = 464; + this.state = 469; this.packageFunctionExport(); } break; case QuixosCapabilityParser.CONSTRUCTOR: this.enterOuterAlt(localContext, 3); { - this.state = 465; + this.state = 470; this.packageConstructorExport(); } break; @@ -1716,61 +1730,61 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 468; + this.state = 473; this.match(QuixosCapabilityParser.OPERATION); - this.state = 469; + this.state = 474; this.identifier(); - this.state = 471; + this.state = 476; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 107) { + if (_la === 108) { { - this.state = 470; + this.state = 475; this.typeParameters(); } } - this.state = 473; - this.match(QuixosCapabilityParser.ID); - this.state = 474; - this.stringLiteral(); - this.state = 475; - this.match(QuixosCapabilityParser.COLON); - this.state = 476; - this.valueType(); - this.state = 477; - this.match(QuixosCapabilityParser.ARROW); this.state = 478; - this.valueType(); + this.match(QuixosCapabilityParser.ID); this.state = 479; - this.match(QuixosCapabilityParser.MODE); + this.stringLiteral(); this.state = 480; - this.operationMode(); + this.match(QuixosCapabilityParser.COLON); + this.state = 481; + this.valueType(); this.state = 482; + this.match(QuixosCapabilityParser.ARROW); + this.state = 483; + this.valueType(); + this.state = 484; + this.match(QuixosCapabilityParser.MODE); + this.state = 485; + this.operationMode(); + this.state = 487; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 51) { + if (_la === 52) { { - this.state = 481; + this.state = 486; this.eventClause(); } } - this.state = 484; + this.state = 489; this.match(QuixosCapabilityParser.RECEIVER); - this.state = 485; + this.state = 490; this.receiverRequirement(); - this.state = 487; + this.state = 492; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 53) { + if (_la === 54) { { - this.state = 486; + this.state = 491; this.dependencyBlock(); } } - this.state = 489; + this.state = 494; this.match(QuixosCapabilityParser.SEMI); } } @@ -1794,43 +1808,43 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 491; + this.state = 496; this.match(QuixosCapabilityParser.FUNCTION); - this.state = 492; + this.state = 497; this.identifier(); - this.state = 494; + this.state = 499; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 107) { + if (_la === 108) { { - this.state = 493; + this.state = 498; this.typeParameters(); } } - this.state = 496; - this.match(QuixosCapabilityParser.ID); - this.state = 497; - this.stringLiteral(); - this.state = 498; - this.match(QuixosCapabilityParser.COLON); - this.state = 499; - this.valueType(); - this.state = 500; - this.match(QuixosCapabilityParser.ARROW); this.state = 501; - this.valueType(); + this.match(QuixosCapabilityParser.ID); + this.state = 502; + this.stringLiteral(); this.state = 503; + this.match(QuixosCapabilityParser.COLON); + this.state = 504; + this.valueType(); + this.state = 505; + this.match(QuixosCapabilityParser.ARROW); + this.state = 506; + this.valueType(); + this.state = 508; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 53) { + if (_la === 54) { { - this.state = 502; + this.state = 507; this.dependencyBlock(); } } - this.state = 505; + this.state = 510; this.match(QuixosCapabilityParser.SEMI); } } @@ -1854,33 +1868,33 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 507; - this.match(QuixosCapabilityParser.CONSTRUCTOR); - this.state = 508; - this.identifier(); - this.state = 509; - this.match(QuixosCapabilityParser.ID); - this.state = 510; - this.stringLiteral(); - this.state = 511; - this.match(QuixosCapabilityParser.CONSTRUCTS); this.state = 512; - this.identifier(); + this.match(QuixosCapabilityParser.CONSTRUCTOR); this.state = 513; - this.match(QuixosCapabilityParser.COLON); + this.identifier(); this.state = 514; - this.valueType(); + this.match(QuixosCapabilityParser.ID); + this.state = 515; + this.stringLiteral(); this.state = 516; + this.match(QuixosCapabilityParser.CONSTRUCTS); + this.state = 517; + this.identifier(); + this.state = 518; + this.match(QuixosCapabilityParser.COLON); + this.state = 519; + this.valueType(); + this.state = 521; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 53) { + if (_la === 54) { { - this.state = 515; + this.state = 520; this.dependencyBlock(); } } - this.state = 518; + this.state = 523; this.match(QuixosCapabilityParser.SEMI); } } @@ -1903,9 +1917,9 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 520; + this.state = 525; this.match(QuixosCapabilityParser.EMITS); - this.state = 521; + this.state = 526; this.valueType(); } } @@ -1929,9 +1943,9 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 523; + this.state = 528; _la = this.tokenStream.LA(1); - if(!(((((_la - 65)) & ~0x1F) === 0 && ((1 << (_la - 65)) & 31) !== 0))) { + if(!(((((_la - 66)) & ~0x1F) === 0 && ((1 << (_la - 66)) & 31) !== 0))) { this.errorHandler.recoverInline(this); } else { @@ -1958,68 +1972,68 @@ export class QuixosCapabilityParser extends antlr.Parser { this.enterRule(localContext, 62, QuixosCapabilityParser.RULE_receiverRequirement); let _la: number; try { - this.state = 543; + this.state = 548; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.ANY: this.enterOuterAlt(localContext, 1); { - this.state = 525; + this.state = 530; this.match(QuixosCapabilityParser.ANY); } break; case QuixosCapabilityParser.ATOM: this.enterOuterAlt(localContext, 2); { - this.state = 526; + this.state = 531; this.match(QuixosCapabilityParser.ATOM); - this.state = 527; + this.state = 532; this.identifier(); } break; case QuixosCapabilityParser.OBJECT: this.enterOuterAlt(localContext, 3); { - this.state = 528; + this.state = 533; this.match(QuixosCapabilityParser.OBJECT); - this.state = 529; + this.state = 534; this.identifier(); } break; case QuixosCapabilityParser.INTERFACES: this.enterOuterAlt(localContext, 4); { - this.state = 530; + this.state = 535; this.match(QuixosCapabilityParser.INTERFACES); - this.state = 531; + this.state = 536; this.match(QuixosCapabilityParser.LBRACK); - this.state = 540; + this.state = 545; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 39 || _la === 113) { + if (_la === 40 || _la === 114) { { - this.state = 532; - this.interfaceType(); this.state = 537; + this.interfaceType(); + this.state = 542; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (_la === 99) { + while (_la === 100) { { { - this.state = 533; + this.state = 538; this.match(QuixosCapabilityParser.COMMA); - this.state = 534; + this.state = 539; this.interfaceType(); } } - this.state = 539; + this.state = 544; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } } } - this.state = 542; + this.state = 547; this.match(QuixosCapabilityParser.RBRACK); } break; @@ -2047,21 +2061,21 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 545; - this.identifier(); this.state = 550; + this.identifier(); + this.state = 555; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (_la === 99) { + while (_la === 100) { { { - this.state = 546; + this.state = 551; this.match(QuixosCapabilityParser.COMMA); - this.state = 547; + this.state = 552; this.identifier(); } } - this.state = 552; + this.state = 557; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } @@ -2087,25 +2101,25 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 553; - this.match(QuixosCapabilityParser.REQUIRES); - this.state = 554; - this.match(QuixosCapabilityParser.LBRACE); this.state = 558; + this.match(QuixosCapabilityParser.REQUIRES); + this.state = 559; + this.match(QuixosCapabilityParser.LBRACE); + this.state = 563; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 402917376) !== 0)) { + while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 805570560) !== 0)) { { { - this.state = 555; + this.state = 560; this.dependencyPort(); } } - this.state = 560; + this.state = 565; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 561; + this.state = 566; this.match(QuixosCapabilityParser.RBRACE); } } @@ -2127,110 +2141,110 @@ export class QuixosCapabilityParser extends antlr.Parser { this.enterRule(localContext, 68, QuixosCapabilityParser.RULE_dependencyPort); let _la: number; try { - this.state = 605; + this.state = 610; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.STATE: this.enterOuterAlt(localContext, 1); { - this.state = 563; - this.match(QuixosCapabilityParser.STATE); - this.state = 564; - this.identifier(); - this.state = 565; - this.match(QuixosCapabilityParser.ID); - this.state = 566; - this.stringLiteral(); - this.state = 567; - this.match(QuixosCapabilityParser.COLON); this.state = 568; - this.valueType(); + this.match(QuixosCapabilityParser.STATE); this.state = 569; - this.primitiveList(); + this.identifier(); this.state = 570; + this.match(QuixosCapabilityParser.ID); + this.state = 571; + this.stringLiteral(); + this.state = 572; + this.match(QuixosCapabilityParser.COLON); + this.state = 573; + this.valueType(); + this.state = 574; + this.primitiveList(); + this.state = 575; this.match(QuixosCapabilityParser.SEMI); } break; case QuixosCapabilityParser.EDGE: this.enterOuterAlt(localContext, 2); { - this.state = 572; - this.match(QuixosCapabilityParser.EDGE); - this.state = 573; - this.identifier(); - this.state = 574; - this.match(QuixosCapabilityParser.ID); - this.state = 575; - this.stringLiteral(); - this.state = 576; - this.match(QuixosCapabilityParser.COLON); this.state = 577; - this.cardinality(); + this.match(QuixosCapabilityParser.EDGE); this.state = 578; - this.targetConstraint(); + this.identifier(); this.state = 579; - this.primitiveList(); + this.match(QuixosCapabilityParser.ID); this.state = 580; + this.stringLiteral(); + this.state = 581; + this.match(QuixosCapabilityParser.COLON); + this.state = 582; + this.cardinality(); + this.state = 583; + this.targetConstraint(); + this.state = 584; + this.primitiveList(); + this.state = 585; this.match(QuixosCapabilityParser.SEMI); } break; case QuixosCapabilityParser.INTERFACE: this.enterOuterAlt(localContext, 3); { - this.state = 582; - this.match(QuixosCapabilityParser.INTERFACE); - this.state = 583; - this.identifier(); - this.state = 584; - this.match(QuixosCapabilityParser.ID); - this.state = 585; - this.stringLiteral(); - this.state = 586; - this.match(QuixosCapabilityParser.COLON); this.state = 587; + this.match(QuixosCapabilityParser.INTERFACE); + this.state = 588; this.identifier(); this.state = 589; + this.match(QuixosCapabilityParser.ID); + this.state = 590; + this.stringLiteral(); + this.state = 591; + this.match(QuixosCapabilityParser.COLON); + this.state = 592; + this.identifier(); + this.state = 594; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 107) { + if (_la === 108) { { - this.state = 588; + this.state = 593; this.typeArguments(); } } - this.state = 591; + this.state = 596; this.match(QuixosCapabilityParser.SEMI); } break; case QuixosCapabilityParser.CONSTRUCTOR: this.enterOuterAlt(localContext, 4); { - this.state = 593; - this.match(QuixosCapabilityParser.CONSTRUCTOR); - this.state = 594; - this.identifier(); - this.state = 595; - this.match(QuixosCapabilityParser.ID); - this.state = 596; - this.stringLiteral(); - this.state = 597; - this.match(QuixosCapabilityParser.COLON); this.state = 598; + this.match(QuixosCapabilityParser.CONSTRUCTOR); + this.state = 599; this.identifier(); + this.state = 600; + this.match(QuixosCapabilityParser.ID); this.state = 601; + this.stringLiteral(); + this.state = 602; + this.match(QuixosCapabilityParser.COLON); + this.state = 603; + this.identifier(); + this.state = 606; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); if (_la === 20) { { - this.state = 599; + this.state = 604; this.match(QuixosCapabilityParser.INPUT); - this.state = 600; + this.state = 605; this.valueType(); } } - this.state = 603; + this.state = 608; this.match(QuixosCapabilityParser.SEMI); } break; @@ -2258,27 +2272,27 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 607; + this.state = 612; this.match(QuixosCapabilityParser.LBRACK); - this.state = 608; - this.primitive(); this.state = 613; + this.primitive(); + this.state = 618; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (_la === 99) { + while (_la === 100) { { { - this.state = 609; + this.state = 614; this.match(QuixosCapabilityParser.COMMA); - this.state = 610; + this.state = 615; this.primitive(); } } - this.state = 615; + this.state = 620; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 616; + this.state = 621; this.match(QuixosCapabilityParser.RBRACK); } } @@ -2302,9 +2316,9 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 618; + this.state = 623; _la = this.tokenStream.LA(1); - if(!(((((_la - 60)) & ~0x1F) === 0 && ((1 << (_la - 60)) & 223) !== 0))) { + if(!(((((_la - 61)) & ~0x1F) === 0 && ((1 << (_la - 61)) & 223) !== 0))) { this.errorHandler.recoverInline(this); } else { @@ -2332,9 +2346,9 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 620; + this.state = 625; this.match(QuixosCapabilityParser.SHARED); - this.state = 621; + this.state = 626; this.attachmentDecl(); } } @@ -2355,20 +2369,20 @@ export class QuixosCapabilityParser extends antlr.Parser { let localContext = new AttachmentDeclContext(this.context, this.state); this.enterRule(localContext, 76, QuixosCapabilityParser.RULE_attachmentDecl); try { - this.state = 625; + this.state = 630; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.STATE: this.enterOuterAlt(localContext, 1); { - this.state = 623; + this.state = 628; this.stateDecl(); } break; case QuixosCapabilityParser.EDGE: this.enterOuterAlt(localContext, 2); { - this.state = 624; + this.state = 629; this.edgeDecl(); } break; @@ -2396,39 +2410,39 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 627; - this.match(QuixosCapabilityParser.STATE); - this.state = 628; - this.identifier(); - this.state = 629; - this.match(QuixosCapabilityParser.ID); - this.state = 630; - this.stringLiteral(); - this.state = 631; - this.match(QuixosCapabilityParser.ON); this.state = 632; - this.identifier(); + this.match(QuixosCapabilityParser.STATE); this.state = 633; - this.match(QuixosCapabilityParser.COLON); + this.identifier(); this.state = 634; - this.valueType(); + this.match(QuixosCapabilityParser.ID); this.state = 635; - this.match(QuixosCapabilityParser.POLICY); + this.stringLiteral(); this.state = 636; - this.storagePolicy(); + this.match(QuixosCapabilityParser.ON); + this.state = 637; + this.identifier(); + this.state = 638; + this.match(QuixosCapabilityParser.COLON); this.state = 639; + this.valueType(); + this.state = 640; + this.match(QuixosCapabilityParser.POLICY); + this.state = 641; + this.storagePolicy(); + this.state = 644; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 38) { + if (_la === 39) { { - this.state = 637; + this.state = 642; this.match(QuixosCapabilityParser.DEFAULT); - this.state = 638; + this.state = 643; this.jsonLiteral(); } } - this.state = 641; + this.state = 646; this.match(QuixosCapabilityParser.SEMI); } } @@ -2449,26 +2463,26 @@ export class QuixosCapabilityParser extends antlr.Parser { let localContext = new StoragePolicyContext(this.context, this.state); this.enterRule(localContext, 80, QuixosCapabilityParser.RULE_storagePolicy); try { - this.state = 649; + this.state = 654; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.OPTIMISTIC_REGISTER: this.enterOuterAlt(localContext, 1); { - this.state = 643; + this.state = 648; this.match(QuixosCapabilityParser.OPTIMISTIC_REGISTER); } break; case QuixosCapabilityParser.CRDT: this.enterOuterAlt(localContext, 2); { - this.state = 644; + this.state = 649; this.match(QuixosCapabilityParser.CRDT); - this.state = 645; + this.state = 650; this.match(QuixosCapabilityParser.LPAREN); - this.state = 646; + this.state = 651; this.valueType(); - this.state = 647; + this.state = 652; this.match(QuixosCapabilityParser.RPAREN); } break; @@ -2495,21 +2509,21 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 651; - this.match(QuixosCapabilityParser.EDGE); - this.state = 652; - this.identifier(); - this.state = 653; - this.match(QuixosCapabilityParser.ID); - this.state = 654; - this.stringLiteral(); - this.state = 655; - this.match(QuixosCapabilityParser.LBRACE); this.state = 656; - this.edgeEndpoint(); + this.match(QuixosCapabilityParser.EDGE); this.state = 657; - this.edgeEndpoint(); + this.identifier(); this.state = 658; + this.match(QuixosCapabilityParser.ID); + this.state = 659; + this.stringLiteral(); + this.state = 660; + this.match(QuixosCapabilityParser.LBRACE); + this.state = 661; + this.edgeEndpoint(); + this.state = 662; + this.edgeEndpoint(); + this.state = 663; this.match(QuixosCapabilityParser.RBRACE); } } @@ -2533,73 +2547,73 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 660; - this.targetConstraint(); - this.state = 661; - this.match(QuixosCapabilityParser.PROJECTION); - this.state = 662; - this.identifier(); - this.state = 663; - this.match(QuixosCapabilityParser.ID); - this.state = 664; - this.stringLiteral(); this.state = 665; - this.cardinality(); + this.targetConstraint(); + this.state = 666; + this.match(QuixosCapabilityParser.PROJECTION); this.state = 667; + this.identifier(); + this.state = 668; + this.match(QuixosCapabilityParser.ID); + this.state = 669; + this.stringLiteral(); + this.state = 670; + this.cardinality(); + this.state = 672; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 76) { + if (_la === 77) { { - this.state = 666; + this.state = 671; this.match(QuixosCapabilityParser.ORDERED); } } - this.state = 671; - this.errorHandler.sync(this); - _la = this.tokenStream.LA(1); - if (_la === 44) { - { - this.state = 669; - this.match(QuixosCapabilityParser.ON_DELETE); - this.state = 670; - this.stringLiteral(); - } - } - - this.state = 674; + this.state = 676; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); if (_la === 45) { { - this.state = 673; - this.match(QuixosCapabilityParser.RETAIN_OTHER); - } - } - - this.state = 678; - this.errorHandler.sync(this); - _la = this.tokenStream.LA(1); - if (_la === 46) { - { - this.state = 676; - this.match(QuixosCapabilityParser.KEYED); - this.state = 677; + this.state = 674; + this.match(QuixosCapabilityParser.ON_DELETE); + this.state = 675; this.stringLiteral(); } } - this.state = 681; + this.state = 679; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 47) { + if (_la === 46) { { - this.state = 680; - this.match(QuixosCapabilityParser.PUBLIC_TRAVERSAL); + this.state = 678; + this.match(QuixosCapabilityParser.RETAIN_OTHER); } } this.state = 683; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + if (_la === 47) { + { + this.state = 681; + this.match(QuixosCapabilityParser.KEYED); + this.state = 682; + this.stringLiteral(); + } + } + + this.state = 686; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + if (_la === 48) { + { + this.state = 685; + this.match(QuixosCapabilityParser.PUBLIC_TRAVERSAL); + } + } + + this.state = 688; this.match(QuixosCapabilityParser.SEMI); } } @@ -2623,65 +2637,65 @@ export class QuixosCapabilityParser extends antlr.Parser { try { this.enterOuterAlt(localContext, 1); { - this.state = 685; - this.match(QuixosCapabilityParser.CONFORM); - this.state = 686; - this.identifier(); - this.state = 687; - this.match(QuixosCapabilityParser.AS); - this.state = 688; - this.identifier(); this.state = 690; + this.match(QuixosCapabilityParser.CONFORM); + this.state = 691; + this.identifier(); + this.state = 692; + this.match(QuixosCapabilityParser.AS); + this.state = 693; + this.identifier(); + this.state = 695; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 107) { + if (_la === 108) { { - this.state = 689; + this.state = 694; this.typeArguments(); } } - this.state = 694; + this.state = 699; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 48) { + if (_la === 49) { { - this.state = 692; + this.state = 697; this.match(QuixosCapabilityParser.ID); - this.state = 693; + this.state = 698; this.stringLiteral(); } } - this.state = 698; + this.state = 703; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 43) { + if (_la === 44) { { - this.state = 696; + this.state = 701; this.match(QuixosCapabilityParser.SEMANTIC_MAJOR); - this.state = 697; + this.state = 702; this.match(QuixosCapabilityParser.INTEGER); } } - this.state = 700; + this.state = 705; this.match(QuixosCapabilityParser.LBRACE); - this.state = 704; + this.state = 709; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (((((_la - 23)) & ~0x1F) === 0 && ((1 << (_la - 23)) & 1029) !== 0)) { + while (((((_la - 23)) & ~0x1F) === 0 && ((1 << (_la - 23)) & 2057) !== 0)) { { { - this.state = 701; + this.state = 706; this.conformanceItem(); } } - this.state = 706; + this.state = 711; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 707; + this.state = 712; this.match(QuixosCapabilityParser.RBRACE); } } @@ -2702,34 +2716,72 @@ export class QuixosCapabilityParser extends antlr.Parser { let localContext = new ConformanceItemContext(this.context, this.state); this.enterRule(localContext, 88, QuixosCapabilityParser.RULE_conformanceItem); try { - this.state = 713; + this.state = 719; this.errorHandler.sync(this); - switch (this.tokenStream.LA(1)) { - case QuixosCapabilityParser.PRIVATE: + switch (this.interpreter.adaptivePredict(this.tokenStream, 61, this.context) ) { + case 1: this.enterOuterAlt(localContext, 1); { - this.state = 709; + this.state = 714; this.match(QuixosCapabilityParser.PRIVATE); - this.state = 710; + this.state = 715; this.attachmentDecl(); } break; - case QuixosCapabilityParser.BIND: + case 2: this.enterOuterAlt(localContext, 2); { - this.state = 711; + this.state = 716; this.operationBindingDecl(); } break; - case QuixosCapabilityParser.MATERIALIZE: + case 3: this.enterOuterAlt(localContext, 3); { - this.state = 712; + this.state = 717; + this.stateFieldBindingDecl(); + } + break; + case 4: + this.enterOuterAlt(localContext, 4); + { + this.state = 718; this.relationshipMaterializationDecl(); } break; - default: - throw new antlr.NoViableAltException(this); + } + } + catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return localContext; + } + public stateFieldBindingDecl(): StateFieldBindingDeclContext { + let localContext = new StateFieldBindingDeclContext(this.context, this.state); + this.enterRule(localContext, 90, QuixosCapabilityParser.RULE_stateFieldBindingDecl); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 721; + this.match(QuixosCapabilityParser.BIND); + this.state = 722; + this.identifier(); + this.state = 723; + this.match(QuixosCapabilityParser.TO); + this.state = 724; + this.match(QuixosCapabilityParser.STATE); + this.state = 725; + this.identifier(); + this.state = 726; + this.match(QuixosCapabilityParser.SEMI); } } catch (re) { @@ -2747,35 +2799,35 @@ export class QuixosCapabilityParser extends antlr.Parser { } public relationshipMaterializationDecl(): RelationshipMaterializationDeclContext { let localContext = new RelationshipMaterializationDeclContext(this.context, this.state); - this.enterRule(localContext, 90, QuixosCapabilityParser.RULE_relationshipMaterializationDecl); + this.enterRule(localContext, 92, QuixosCapabilityParser.RULE_relationshipMaterializationDecl); try { this.enterOuterAlt(localContext, 1); { - this.state = 715; + this.state = 728; this.match(QuixosCapabilityParser.MATERIALIZE); - this.state = 716; + this.state = 729; this.identifier(); - this.state = 717; + this.state = 730; this.match(QuixosCapabilityParser.IF); - this.state = 718; + this.state = 731; this.match(QuixosCapabilityParser.ABSENT); - this.state = 719; + this.state = 732; this.match(QuixosCapabilityParser.USING); - this.state = 720; + this.state = 733; this.match(QuixosCapabilityParser.CONSTRUCTOR); - this.state = 721; + this.state = 734; this.identifier(); - this.state = 722; + this.state = 735; this.match(QuixosCapabilityParser.VIA); - this.state = 723; + this.state = 736; this.match(QuixosCapabilityParser.EDGE); - this.state = 724; + this.state = 737; this.identifier(); - this.state = 725; + this.state = 738; this.match(QuixosCapabilityParser.DOT); - this.state = 726; + this.state = 739; this.identifier(); - this.state = 727; + this.state = 740; this.match(QuixosCapabilityParser.SEMI); } } @@ -2794,19 +2846,19 @@ export class QuixosCapabilityParser extends antlr.Parser { } public operationBindingDecl(): OperationBindingDeclContext { let localContext = new OperationBindingDeclContext(this.context, this.state); - this.enterRule(localContext, 92, QuixosCapabilityParser.RULE_operationBindingDecl); + this.enterRule(localContext, 94, QuixosCapabilityParser.RULE_operationBindingDecl); try { this.enterOuterAlt(localContext, 1); { - this.state = 729; + this.state = 742; this.match(QuixosCapabilityParser.BIND); - this.state = 730; + this.state = 743; this.memberOperationRef(); - this.state = 731; + this.state = 744; this.match(QuixosCapabilityParser.TO); - this.state = 732; + this.state = 745; this.operationProvider(); - this.state = 733; + this.state = 746; this.match(QuixosCapabilityParser.SEMI); } } @@ -2825,15 +2877,15 @@ export class QuixosCapabilityParser extends antlr.Parser { } public memberOperationRef(): MemberOperationRefContext { let localContext = new MemberOperationRefContext(this.context, this.state); - this.enterRule(localContext, 94, QuixosCapabilityParser.RULE_memberOperationRef); + this.enterRule(localContext, 96, QuixosCapabilityParser.RULE_memberOperationRef); try { this.enterOuterAlt(localContext, 1); { - this.state = 735; + this.state = 748; this.identifier(); - this.state = 736; + this.state = 749; this.match(QuixosCapabilityParser.DOT); - this.state = 737; + this.state = 750; this.operationName(); } } @@ -2852,86 +2904,86 @@ export class QuixosCapabilityParser extends antlr.Parser { } public operationName(): OperationNameContext { let localContext = new OperationNameContext(this.context, this.state); - this.enterRule(localContext, 96, QuixosCapabilityParser.RULE_operationName); + this.enterRule(localContext, 98, QuixosCapabilityParser.RULE_operationName); try { - this.state = 750; + this.state = 763; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.SOURCE: case QuixosCapabilityParser.IDENTIFIER: this.enterOuterAlt(localContext, 1); { - this.state = 739; + this.state = 752; this.identifier(); } break; case QuixosCapabilityParser.CALL: this.enterOuterAlt(localContext, 2); { - this.state = 740; + this.state = 753; this.match(QuixosCapabilityParser.CALL); } break; case QuixosCapabilityParser.GET: this.enterOuterAlt(localContext, 3); { - this.state = 741; + this.state = 754; this.match(QuixosCapabilityParser.GET); } break; case QuixosCapabilityParser.SET: this.enterOuterAlt(localContext, 4); { - this.state = 742; + this.state = 755; this.match(QuixosCapabilityParser.SET); } break; case QuixosCapabilityParser.RESOLVE: this.enterOuterAlt(localContext, 5); { - this.state = 743; + this.state = 756; this.match(QuixosCapabilityParser.RESOLVE); } break; case QuixosCapabilityParser.CONNECT: this.enterOuterAlt(localContext, 6); { - this.state = 744; + this.state = 757; this.match(QuixosCapabilityParser.CONNECT); } break; case QuixosCapabilityParser.DISCONNECT: this.enterOuterAlt(localContext, 7); { - this.state = 745; + this.state = 758; this.match(QuixosCapabilityParser.DISCONNECT); } break; case QuixosCapabilityParser.WATCH_START: this.enterOuterAlt(localContext, 8); { - this.state = 746; + this.state = 759; this.match(QuixosCapabilityParser.WATCH_START); } break; case QuixosCapabilityParser.WATCH_STOP: this.enterOuterAlt(localContext, 9); { - this.state = 747; + this.state = 760; this.match(QuixosCapabilityParser.WATCH_STOP); } break; case QuixosCapabilityParser.SUBSCRIBE: this.enterOuterAlt(localContext, 10); { - this.state = 748; + this.state = 761; this.match(QuixosCapabilityParser.SUBSCRIBE); } break; case QuixosCapabilityParser.UNSUBSCRIBE: this.enterOuterAlt(localContext, 11); { - this.state = 749; + this.state = 762; this.match(QuixosCapabilityParser.UNSUBSCRIBE); } break; @@ -2954,69 +3006,69 @@ export class QuixosCapabilityParser extends antlr.Parser { } public operationProvider(): OperationProviderContext { let localContext = new OperationProviderContext(this.context, this.state); - this.enterRule(localContext, 98, QuixosCapabilityParser.RULE_operationProvider); + this.enterRule(localContext, 100, QuixosCapabilityParser.RULE_operationProvider); let _la: number; try { - this.state = 774; + this.state = 787; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.STATE: this.enterOuterAlt(localContext, 1); { - this.state = 752; + this.state = 765; this.match(QuixosCapabilityParser.STATE); - this.state = 753; + this.state = 766; this.identifier(); - this.state = 754; + this.state = 767; this.match(QuixosCapabilityParser.DOT); - this.state = 755; + this.state = 768; this.statePrimitive(); } break; case QuixosCapabilityParser.EDGE: this.enterOuterAlt(localContext, 2); { - this.state = 757; + this.state = 770; this.match(QuixosCapabilityParser.EDGE); - this.state = 758; + this.state = 771; this.identifier(); - this.state = 759; + this.state = 772; this.match(QuixosCapabilityParser.DOT); - this.state = 760; + this.state = 773; this.identifier(); - this.state = 761; + this.state = 774; this.match(QuixosCapabilityParser.DOT); - this.state = 762; + this.state = 775; this.edgePrimitive(); } break; case QuixosCapabilityParser.PACKAGE: this.enterOuterAlt(localContext, 3); { - this.state = 764; + this.state = 777; this.match(QuixosCapabilityParser.PACKAGE); - this.state = 765; + this.state = 778; this.identifier(); - this.state = 766; + this.state = 779; this.match(QuixosCapabilityParser.DOT); - this.state = 767; + this.state = 780; this.identifier(); - this.state = 769; + this.state = 782; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 107) { + if (_la === 108) { { - this.state = 768; + this.state = 781; this.typeArguments(); } } - this.state = 772; + this.state = 785; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 30) { + if (_la === 31) { { - this.state = 771; + this.state = 784; this.dependencyBindingBlock(); } } @@ -3042,14 +3094,14 @@ export class QuixosCapabilityParser extends antlr.Parser { } public statePrimitive(): StatePrimitiveContext { let localContext = new StatePrimitiveContext(this.context, this.state); - this.enterRule(localContext, 100, QuixosCapabilityParser.RULE_statePrimitive); + this.enterRule(localContext, 102, QuixosCapabilityParser.RULE_statePrimitive); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 776; + this.state = 789; _la = this.tokenStream.LA(1); - if(!(((((_la - 60)) & ~0x1F) === 0 && ((1 << (_la - 60)) & 195) !== 0))) { + if(!(((((_la - 61)) & ~0x1F) === 0 && ((1 << (_la - 61)) & 195) !== 0))) { this.errorHandler.recoverInline(this); } else { @@ -3073,14 +3125,14 @@ export class QuixosCapabilityParser extends antlr.Parser { } public edgePrimitive(): EdgePrimitiveContext { let localContext = new EdgePrimitiveContext(this.context, this.state); - this.enterRule(localContext, 102, QuixosCapabilityParser.RULE_edgePrimitive); + this.enterRule(localContext, 104, QuixosCapabilityParser.RULE_edgePrimitive); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 778; + this.state = 791; _la = this.tokenStream.LA(1); - if(!(((((_la - 62)) & ~0x1F) === 0 && ((1 << (_la - 62)) & 55) !== 0))) { + if(!(((((_la - 63)) & ~0x1F) === 0 && ((1 << (_la - 63)) & 55) !== 0))) { this.errorHandler.recoverInline(this); } else { @@ -3104,30 +3156,30 @@ export class QuixosCapabilityParser extends antlr.Parser { } public dependencyBindingBlock(): DependencyBindingBlockContext { let localContext = new DependencyBindingBlockContext(this.context, this.state); - this.enterRule(localContext, 104, QuixosCapabilityParser.RULE_dependencyBindingBlock); + this.enterRule(localContext, 106, QuixosCapabilityParser.RULE_dependencyBindingBlock); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 780; + this.state = 793; this.match(QuixosCapabilityParser.WITH); - this.state = 781; + this.state = 794; this.match(QuixosCapabilityParser.LBRACE); - this.state = 785; + this.state = 798; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (_la === 39 || _la === 113) { + while (_la === 40 || _la === 114) { { { - this.state = 782; + this.state = 795; this.dependencyBinding(); } } - this.state = 787; + this.state = 800; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 788; + this.state = 801; this.match(QuixosCapabilityParser.RBRACE); } } @@ -3146,137 +3198,137 @@ export class QuixosCapabilityParser extends antlr.Parser { } public dependencyBinding(): DependencyBindingContext { let localContext = new DependencyBindingContext(this.context, this.state); - this.enterRule(localContext, 106, QuixosCapabilityParser.RULE_dependencyBinding); + this.enterRule(localContext, 108, QuixosCapabilityParser.RULE_dependencyBinding); let _la: number; try { - this.state = 843; + this.state = 856; this.errorHandler.sync(this); - switch (this.interpreter.adaptivePredict(this.tokenStream, 70, this.context) ) { + switch (this.interpreter.adaptivePredict(this.tokenStream, 71, this.context) ) { case 1: this.enterOuterAlt(localContext, 1); { - this.state = 790; + this.state = 803; this.identifier(); - this.state = 791; + this.state = 804; this.match(QuixosCapabilityParser.TO); - this.state = 792; + this.state = 805; this.match(QuixosCapabilityParser.STATE); - this.state = 793; + this.state = 806; this.identifier(); - this.state = 800; + this.state = 813; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 32) { + if (_la === 33) { { - this.state = 794; + this.state = 807; this.match(QuixosCapabilityParser.VIA); - this.state = 795; + this.state = 808; this.match(QuixosCapabilityParser.EDGE); - this.state = 796; + this.state = 809; this.identifier(); - this.state = 797; + this.state = 810; this.match(QuixosCapabilityParser.DOT); - this.state = 798; + this.state = 811; this.identifier(); } } - this.state = 802; + this.state = 815; this.match(QuixosCapabilityParser.SEMI); } break; case 2: this.enterOuterAlt(localContext, 2); { - this.state = 804; + this.state = 817; this.identifier(); - this.state = 805; + this.state = 818; this.match(QuixosCapabilityParser.TO); - this.state = 806; + this.state = 819; this.match(QuixosCapabilityParser.EDGE); - this.state = 807; + this.state = 820; this.identifier(); - this.state = 808; + this.state = 821; this.match(QuixosCapabilityParser.DOT); - this.state = 809; + this.state = 822; this.identifier(); - this.state = 816; + this.state = 829; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 32) { + if (_la === 33) { { - this.state = 810; + this.state = 823; this.match(QuixosCapabilityParser.VIA); - this.state = 811; + this.state = 824; this.match(QuixosCapabilityParser.EDGE); - this.state = 812; + this.state = 825; this.identifier(); - this.state = 813; + this.state = 826; this.match(QuixosCapabilityParser.DOT); - this.state = 814; + this.state = 827; this.identifier(); } } - this.state = 818; + this.state = 831; this.match(QuixosCapabilityParser.SEMI); } break; case 3: this.enterOuterAlt(localContext, 3); { - this.state = 820; + this.state = 833; this.identifier(); - this.state = 821; + this.state = 834; this.match(QuixosCapabilityParser.TO); - this.state = 822; + this.state = 835; this.match(QuixosCapabilityParser.INTERFACE); - this.state = 823; + this.state = 836; this.identifier(); - this.state = 825; + this.state = 838; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 107) { + if (_la === 108) { { - this.state = 824; + this.state = 837; this.typeArguments(); } } - this.state = 833; + this.state = 846; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 32) { + if (_la === 33) { { - this.state = 827; + this.state = 840; this.match(QuixosCapabilityParser.VIA); - this.state = 828; + this.state = 841; this.match(QuixosCapabilityParser.EDGE); - this.state = 829; + this.state = 842; this.identifier(); - this.state = 830; + this.state = 843; this.match(QuixosCapabilityParser.DOT); - this.state = 831; + this.state = 844; this.identifier(); } } - this.state = 835; + this.state = 848; this.match(QuixosCapabilityParser.SEMI); } break; case 4: this.enterOuterAlt(localContext, 4); { - this.state = 837; + this.state = 850; this.identifier(); - this.state = 838; + this.state = 851; this.match(QuixosCapabilityParser.TO); - this.state = 839; + this.state = 852; this.match(QuixosCapabilityParser.CONSTRUCTOR); - this.state = 840; + this.state = 853; this.identifier(); - this.state = 841; + this.state = 854; this.match(QuixosCapabilityParser.SEMI); } break; @@ -3297,34 +3349,34 @@ export class QuixosCapabilityParser extends antlr.Parser { } public constructorBindingDecl(): ConstructorBindingDeclContext { let localContext = new ConstructorBindingDeclContext(this.context, this.state); - this.enterRule(localContext, 108, QuixosCapabilityParser.RULE_constructorBindingDecl); + this.enterRule(localContext, 110, QuixosCapabilityParser.RULE_constructorBindingDecl); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 845; + this.state = 858; this.match(QuixosCapabilityParser.CONSTRUCTOR); - this.state = 846; + this.state = 859; this.identifier(); - this.state = 847; + this.state = 860; this.match(QuixosCapabilityParser.TO); - this.state = 848; + this.state = 861; this.identifier(); - this.state = 849; + this.state = 862; this.match(QuixosCapabilityParser.DOT); - this.state = 850; + this.state = 863; this.identifier(); - this.state = 852; + this.state = 865; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 30) { + if (_la === 31) { { - this.state = 851; + this.state = 864; this.dependencyBindingBlock(); } } - this.state = 854; + this.state = 867; this.match(QuixosCapabilityParser.SEMI); } } @@ -3343,10 +3395,10 @@ export class QuixosCapabilityParser extends antlr.Parser { } public valueType(): ValueTypeContext { let localContext = new ValueTypeContext(this.context, this.state); - this.enterRule(localContext, 110, QuixosCapabilityParser.RULE_valueType); + this.enterRule(localContext, 112, QuixosCapabilityParser.RULE_valueType); let _la: number; try { - this.state = 902; + this.state = 915; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.BOOL: @@ -3359,74 +3411,38 @@ export class QuixosCapabilityParser extends antlr.Parser { case QuixosCapabilityParser.UINT64: this.enterOuterAlt(localContext, 1); { - this.state = 856; + this.state = 869; this.scalarType(); } break; case QuixosCapabilityParser.UNIT: this.enterOuterAlt(localContext, 2); { - this.state = 857; + this.state = 870; this.match(QuixosCapabilityParser.UNIT); } break; case QuixosCapabilityParser.WATCH_HANDLE: this.enterOuterAlt(localContext, 3); { - this.state = 858; + this.state = 871; this.match(QuixosCapabilityParser.WATCH_HANDLE); } break; case QuixosCapabilityParser.MESSAGE: this.enterOuterAlt(localContext, 4); { - this.state = 859; + this.state = 872; this.match(QuixosCapabilityParser.MESSAGE); - this.state = 860; + this.state = 873; this.stringLiteral(); } break; case QuixosCapabilityParser.ATOM_REF: this.enterOuterAlt(localContext, 5); { - this.state = 861; - this.match(QuixosCapabilityParser.ATOM_REF); - this.state = 862; - this.match(QuixosCapabilityParser.LT); - this.state = 863; - this.identifier(); - this.state = 864; - this.match(QuixosCapabilityParser.GT); - } - break; - case QuixosCapabilityParser.INTERFACE_REF: - this.enterOuterAlt(localContext, 6); - { - this.state = 866; - this.match(QuixosCapabilityParser.INTERFACE_REF); - this.state = 867; - this.match(QuixosCapabilityParser.LT); - this.state = 868; - this.identifier(); - this.state = 870; - this.errorHandler.sync(this); - _la = this.tokenStream.LA(1); - if (_la === 107) { - { - this.state = 869; - this.typeArguments(); - } - } - - this.state = 872; - this.match(QuixosCapabilityParser.GT); - } - break; - case QuixosCapabilityParser.REF: - this.enterOuterAlt(localContext, 7); - { this.state = 874; - this.match(QuixosCapabilityParser.REF); + this.match(QuixosCapabilityParser.ATOM_REF); this.state = 875; this.match(QuixosCapabilityParser.LT); this.state = 876; @@ -3435,54 +3451,90 @@ export class QuixosCapabilityParser extends antlr.Parser { this.match(QuixosCapabilityParser.GT); } break; - case QuixosCapabilityParser.OPTIONAL: - this.enterOuterAlt(localContext, 8); + case QuixosCapabilityParser.INTERFACE_REF: + this.enterOuterAlt(localContext, 6); { this.state = 879; - this.match(QuixosCapabilityParser.OPTIONAL); + this.match(QuixosCapabilityParser.INTERFACE_REF); this.state = 880; this.match(QuixosCapabilityParser.LT); this.state = 881; + this.identifier(); + this.state = 883; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + if (_la === 108) { + { + this.state = 882; + this.typeArguments(); + } + } + + this.state = 885; + this.match(QuixosCapabilityParser.GT); + } + break; + case QuixosCapabilityParser.REF: + this.enterOuterAlt(localContext, 7); + { + this.state = 887; + this.match(QuixosCapabilityParser.REF); + this.state = 888; + this.match(QuixosCapabilityParser.LT); + this.state = 889; + this.identifier(); + this.state = 890; + this.match(QuixosCapabilityParser.GT); + } + break; + case QuixosCapabilityParser.OPTIONAL: + this.enterOuterAlt(localContext, 8); + { + this.state = 892; + this.match(QuixosCapabilityParser.OPTIONAL); + this.state = 893; + this.match(QuixosCapabilityParser.LT); + this.state = 894; this.valueType(); - this.state = 882; + this.state = 895; this.match(QuixosCapabilityParser.GT); } break; case QuixosCapabilityParser.LIST: this.enterOuterAlt(localContext, 9); { - this.state = 884; + this.state = 897; this.match(QuixosCapabilityParser.LIST); - this.state = 885; + this.state = 898; this.match(QuixosCapabilityParser.LT); - this.state = 886; + this.state = 899; this.valueType(); - this.state = 887; + this.state = 900; this.match(QuixosCapabilityParser.GT); } break; case QuixosCapabilityParser.RECORD: this.enterOuterAlt(localContext, 10); { - this.state = 889; + this.state = 902; this.match(QuixosCapabilityParser.RECORD); - this.state = 890; + this.state = 903; this.match(QuixosCapabilityParser.LBRACE); - this.state = 894; + this.state = 907; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (_la === 39 || _la === 113) { + while (_la === 40 || _la === 114) { { { - this.state = 891; + this.state = 904; this.recordField(); } } - this.state = 896; + this.state = 909; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } - this.state = 897; + this.state = 910; this.match(QuixosCapabilityParser.RBRACE); } break; @@ -3490,14 +3542,14 @@ export class QuixosCapabilityParser extends antlr.Parser { case QuixosCapabilityParser.IDENTIFIER: this.enterOuterAlt(localContext, 11); { - this.state = 898; + this.state = 911; this.identifier(); - this.state = 900; + this.state = 913; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 107) { + if (_la === 108) { { - this.state = 899; + this.state = 912; this.typeArguments(); } } @@ -3523,17 +3575,17 @@ export class QuixosCapabilityParser extends antlr.Parser { } public recordField(): RecordFieldContext { let localContext = new RecordFieldContext(this.context, this.state); - this.enterRule(localContext, 112, QuixosCapabilityParser.RULE_recordField); + this.enterRule(localContext, 114, QuixosCapabilityParser.RULE_recordField); try { this.enterOuterAlt(localContext, 1); { - this.state = 904; + this.state = 917; this.identifier(); - this.state = 905; + this.state = 918; this.match(QuixosCapabilityParser.COLON); - this.state = 906; + this.state = 919; this.valueType(); - this.state = 907; + this.state = 920; this.match(QuixosCapabilityParser.SEMI); } } @@ -3552,14 +3604,14 @@ export class QuixosCapabilityParser extends antlr.Parser { } public scalarType(): ScalarTypeContext { let localContext = new ScalarTypeContext(this.context, this.state); - this.enterRule(localContext, 114, QuixosCapabilityParser.RULE_scalarType); + this.enterRule(localContext, 116, QuixosCapabilityParser.RULE_scalarType); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 909; + this.state = 922; _la = this.tokenStream.LA(1); - if(!(((((_la - 85)) & ~0x1F) === 0 && ((1 << (_la - 85)) & 255) !== 0))) { + if(!(((((_la - 86)) & ~0x1F) === 0 && ((1 << (_la - 86)) & 255) !== 0))) { this.errorHandler.recoverInline(this); } else { @@ -3583,14 +3635,14 @@ export class QuixosCapabilityParser extends antlr.Parser { } public cardinality(): CardinalityContext { let localContext = new CardinalityContext(this.context, this.state); - this.enterRule(localContext, 116, QuixosCapabilityParser.RULE_cardinality); + this.enterRule(localContext, 118, QuixosCapabilityParser.RULE_cardinality); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 911; + this.state = 924; _la = this.tokenStream.LA(1); - if(!(((((_la - 72)) & ~0x1F) === 0 && ((1 << (_la - 72)) & 15) !== 0))) { + if(!(((((_la - 73)) & ~0x1F) === 0 && ((1 << (_la - 73)) & 15) !== 0))) { this.errorHandler.recoverInline(this); } else { @@ -3614,64 +3666,64 @@ export class QuixosCapabilityParser extends antlr.Parser { } public jsonLiteral(): JsonLiteralContext { let localContext = new JsonLiteralContext(this.context, this.state); - this.enterRule(localContext, 118, QuixosCapabilityParser.RULE_jsonLiteral); + this.enterRule(localContext, 120, QuixosCapabilityParser.RULE_jsonLiteral); try { - this.state = 921; + this.state = 934; this.errorHandler.sync(this); switch (this.tokenStream.LA(1)) { case QuixosCapabilityParser.STRING_LITERAL: this.enterOuterAlt(localContext, 1); { - this.state = 913; + this.state = 926; this.stringLiteral(); } break; case QuixosCapabilityParser.INTEGER: this.enterOuterAlt(localContext, 2); { - this.state = 914; + this.state = 927; this.match(QuixosCapabilityParser.INTEGER); } break; case QuixosCapabilityParser.JSON_NUMBER: this.enterOuterAlt(localContext, 3); { - this.state = 915; + this.state = 928; this.match(QuixosCapabilityParser.JSON_NUMBER); } break; case QuixosCapabilityParser.TRUE: this.enterOuterAlt(localContext, 4); { - this.state = 916; + this.state = 929; this.match(QuixosCapabilityParser.TRUE); } break; case QuixosCapabilityParser.FALSE: this.enterOuterAlt(localContext, 5); { - this.state = 917; + this.state = 930; this.match(QuixosCapabilityParser.FALSE); } break; case QuixosCapabilityParser.NULL: this.enterOuterAlt(localContext, 6); { - this.state = 918; + this.state = 931; this.match(QuixosCapabilityParser.NULL); } break; case QuixosCapabilityParser.LBRACE: this.enterOuterAlt(localContext, 7); { - this.state = 919; + this.state = 932; this.jsonObject(); } break; case QuixosCapabilityParser.LBRACK: this.enterOuterAlt(localContext, 8); { - this.state = 920; + this.state = 933; this.jsonArray(); } break; @@ -3694,40 +3746,40 @@ export class QuixosCapabilityParser extends antlr.Parser { } public jsonObject(): JsonObjectContext { let localContext = new JsonObjectContext(this.context, this.state); - this.enterRule(localContext, 120, QuixosCapabilityParser.RULE_jsonObject); + this.enterRule(localContext, 122, QuixosCapabilityParser.RULE_jsonObject); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 923; + this.state = 936; this.match(QuixosCapabilityParser.LBRACE); - this.state = 932; + this.state = 945; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (_la === 114) { + if (_la === 115) { { - this.state = 924; + this.state = 937; this.jsonMember(); - this.state = 929; + this.state = 942; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (_la === 99) { + while (_la === 100) { { { - this.state = 925; + this.state = 938; this.match(QuixosCapabilityParser.COMMA); - this.state = 926; + this.state = 939; this.jsonMember(); } } - this.state = 931; + this.state = 944; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } } } - this.state = 934; + this.state = 947; this.match(QuixosCapabilityParser.RBRACE); } } @@ -3746,15 +3798,15 @@ export class QuixosCapabilityParser extends antlr.Parser { } public jsonMember(): JsonMemberContext { let localContext = new JsonMemberContext(this.context, this.state); - this.enterRule(localContext, 122, QuixosCapabilityParser.RULE_jsonMember); + this.enterRule(localContext, 124, QuixosCapabilityParser.RULE_jsonMember); try { this.enterOuterAlt(localContext, 1); { - this.state = 936; + this.state = 949; this.stringLiteral(); - this.state = 937; + this.state = 950; this.match(QuixosCapabilityParser.COLON); - this.state = 938; + this.state = 951; this.jsonLiteral(); } } @@ -3773,40 +3825,40 @@ export class QuixosCapabilityParser extends antlr.Parser { } public jsonArray(): JsonArrayContext { let localContext = new JsonArrayContext(this.context, this.state); - this.enterRule(localContext, 124, QuixosCapabilityParser.RULE_jsonArray); + this.enterRule(localContext, 126, QuixosCapabilityParser.RULE_jsonArray); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 940; + this.state = 953; this.match(QuixosCapabilityParser.LBRACK); - this.state = 949; + this.state = 962; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - if (((((_la - 93)) & ~0x1F) === 0 && ((1 << (_la - 93)) & 2884871) !== 0)) { + if (((((_la - 94)) & ~0x1F) === 0 && ((1 << (_la - 94)) & 2884871) !== 0)) { { - this.state = 941; + this.state = 954; this.jsonLiteral(); - this.state = 946; + this.state = 959; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); - while (_la === 99) { + while (_la === 100) { { { - this.state = 942; + this.state = 955; this.match(QuixosCapabilityParser.COMMA); - this.state = 943; + this.state = 956; this.jsonLiteral(); } } - this.state = 948; + this.state = 961; this.errorHandler.sync(this); _la = this.tokenStream.LA(1); } } } - this.state = 951; + this.state = 964; this.match(QuixosCapabilityParser.RBRACK); } } @@ -3825,14 +3877,14 @@ export class QuixosCapabilityParser extends antlr.Parser { } public identifier(): IdentifierContext { let localContext = new IdentifierContext(this.context, this.state); - this.enterRule(localContext, 126, QuixosCapabilityParser.RULE_identifier); + this.enterRule(localContext, 128, QuixosCapabilityParser.RULE_identifier); let _la: number; try { this.enterOuterAlt(localContext, 1); { - this.state = 953; + this.state = 966; _la = this.tokenStream.LA(1); - if(!(_la === 39 || _la === 113)) { + if(!(_la === 40 || _la === 114)) { this.errorHandler.recoverInline(this); } else { @@ -3856,11 +3908,11 @@ export class QuixosCapabilityParser extends antlr.Parser { } public stringLiteral(): StringLiteralContext { let localContext = new StringLiteralContext(this.context, this.state); - this.enterRule(localContext, 128, QuixosCapabilityParser.RULE_stringLiteral); + this.enterRule(localContext, 130, QuixosCapabilityParser.RULE_stringLiteral); try { this.enterOuterAlt(localContext, 1); { - this.state = 955; + this.state = 968; this.match(QuixosCapabilityParser.STRING_LITERAL); } } @@ -3879,7 +3931,7 @@ export class QuixosCapabilityParser extends antlr.Parser { } public static readonly _serializedATN: number[] = [ - 4,1,117,958,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6, + 4,1,118,971,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6, 7,6,2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7, 13,2,14,7,14,2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19,7,19,2, 20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25,2,26,7, @@ -3888,351 +3940,357 @@ export class QuixosCapabilityParser extends antlr.Parser { 39,2,40,7,40,2,41,7,41,2,42,7,42,2,43,7,43,2,44,7,44,2,45,7,45,2, 46,7,46,2,47,7,47,2,48,7,48,2,49,7,49,2,50,7,50,2,51,7,51,2,52,7, 52,2,53,7,53,2,54,7,54,2,55,7,55,2,56,7,56,2,57,7,57,2,58,7,58,2, - 59,7,59,2,60,7,60,2,61,7,61,2,62,7,62,2,63,7,63,2,64,7,64,1,0,1, - 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,3,0,143,8,0,1,1,1,1,1, - 1,5,1,148,8,1,10,1,12,1,151,9,1,1,1,1,1,1,2,1,2,1,2,1,2,1,3,1,3, - 1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,5,3,169,8,3,10,3,12,3,172,9,3,1, - 3,1,3,1,4,1,4,1,4,1,4,1,4,1,4,1,4,3,4,183,8,4,1,5,1,5,1,5,1,5,1, - 5,1,5,1,5,1,5,1,5,1,5,3,5,195,8,5,1,6,1,6,1,6,1,6,1,6,1,6,1,6,1, - 7,1,7,1,7,1,7,1,7,1,7,1,7,1,8,1,8,1,8,1,8,3,8,215,8,8,1,9,1,9,1, - 9,1,9,1,9,1,9,3,9,223,8,9,1,9,1,9,1,10,5,10,228,8,10,10,10,12,10, - 231,9,10,1,10,1,10,1,10,3,10,236,8,10,1,10,1,10,1,10,1,10,1,10,1, - 10,1,10,1,10,5,10,246,8,10,10,10,12,10,249,9,10,3,10,251,8,10,1, - 10,1,10,5,10,255,8,10,10,10,12,10,258,9,10,1,10,1,10,1,11,1,11,1, - 11,1,11,5,11,266,8,11,10,11,12,11,269,9,11,1,11,1,11,1,12,1,12,1, - 12,1,12,3,12,277,8,12,1,12,1,12,1,12,1,12,1,12,1,12,5,12,285,8,12, - 10,12,12,12,288,9,12,3,12,290,8,12,3,12,292,8,12,1,13,1,13,3,13, - 296,8,13,1,14,1,14,1,14,1,14,5,14,302,8,14,10,14,12,14,305,9,14, - 1,14,1,14,1,15,1,15,1,15,1,15,1,15,1,15,1,15,3,15,316,8,15,1,16, - 1,16,1,16,3,16,321,8,16,1,16,1,16,1,16,1,16,1,17,1,17,1,17,3,17, - 330,8,17,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,18, - 1,18,1,18,1,18,1,18,1,19,1,19,1,19,1,19,1,19,1,19,1,19,1,19,5,19, - 355,8,19,10,19,12,19,358,9,19,1,19,1,19,1,20,1,20,1,20,1,20,1,20, - 1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20, - 1,20,3,20,381,8,20,1,21,1,21,1,21,1,21,1,21,1,21,1,21,1,21,3,21, - 391,8,21,1,21,1,21,5,21,395,8,21,10,21,12,21,398,9,21,1,21,1,21, - 1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22, - 1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,3,22,426, - 8,22,1,23,1,23,1,23,1,23,1,23,3,23,433,8,23,1,23,1,23,3,23,437,8, - 23,1,24,5,24,440,8,24,10,24,12,24,443,9,24,1,24,1,24,1,24,1,24,1, - 24,1,24,1,24,1,24,3,24,453,8,24,1,24,1,24,5,24,457,8,24,10,24,12, - 24,460,9,24,1,24,1,24,1,25,1,25,1,25,3,25,467,8,25,1,26,1,26,1,26, - 3,26,472,8,26,1,26,1,26,1,26,1,26,1,26,1,26,1,26,1,26,1,26,3,26, - 483,8,26,1,26,1,26,1,26,3,26,488,8,26,1,26,1,26,1,27,1,27,1,27,3, - 27,495,8,27,1,27,1,27,1,27,1,27,1,27,1,27,1,27,3,27,504,8,27,1,27, - 1,27,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,3,28,517,8,28, - 1,28,1,28,1,29,1,29,1,29,1,30,1,30,1,31,1,31,1,31,1,31,1,31,1,31, - 1,31,1,31,1,31,1,31,5,31,536,8,31,10,31,12,31,539,9,31,3,31,541, - 8,31,1,31,3,31,544,8,31,1,32,1,32,1,32,5,32,549,8,32,10,32,12,32, - 552,9,32,1,33,1,33,1,33,5,33,557,8,33,10,33,12,33,560,9,33,1,33, - 1,33,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34, - 1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34, - 1,34,3,34,590,8,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34, - 1,34,3,34,602,8,34,1,34,1,34,3,34,606,8,34,1,35,1,35,1,35,1,35,5, - 35,612,8,35,10,35,12,35,615,9,35,1,35,1,35,1,36,1,36,1,37,1,37,1, - 37,1,38,1,38,3,38,626,8,38,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1, - 39,1,39,1,39,1,39,1,39,3,39,640,8,39,1,39,1,39,1,40,1,40,1,40,1, - 40,1,40,1,40,3,40,650,8,40,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1, - 41,1,41,1,42,1,42,1,42,1,42,1,42,1,42,1,42,3,42,668,8,42,1,42,1, - 42,3,42,672,8,42,1,42,3,42,675,8,42,1,42,1,42,3,42,679,8,42,1,42, - 3,42,682,8,42,1,42,1,42,1,43,1,43,1,43,1,43,1,43,3,43,691,8,43,1, - 43,1,43,3,43,695,8,43,1,43,1,43,3,43,699,8,43,1,43,1,43,5,43,703, - 8,43,10,43,12,43,706,9,43,1,43,1,43,1,44,1,44,1,44,1,44,3,44,714, - 8,44,1,45,1,45,1,45,1,45,1,45,1,45,1,45,1,45,1,45,1,45,1,45,1,45, - 1,45,1,45,1,46,1,46,1,46,1,46,1,46,1,46,1,47,1,47,1,47,1,47,1,48, - 1,48,1,48,1,48,1,48,1,48,1,48,1,48,1,48,1,48,1,48,3,48,751,8,48, - 1,49,1,49,1,49,1,49,1,49,1,49,1,49,1,49,1,49,1,49,1,49,1,49,1,49, - 1,49,1,49,1,49,1,49,3,49,770,8,49,1,49,3,49,773,8,49,3,49,775,8, - 49,1,50,1,50,1,51,1,51,1,52,1,52,1,52,5,52,784,8,52,10,52,12,52, - 787,9,52,1,52,1,52,1,53,1,53,1,53,1,53,1,53,1,53,1,53,1,53,1,53, - 1,53,3,53,801,8,53,1,53,1,53,1,53,1,53,1,53,1,53,1,53,1,53,1,53, - 1,53,1,53,1,53,1,53,1,53,3,53,817,8,53,1,53,1,53,1,53,1,53,1,53, - 1,53,1,53,3,53,826,8,53,1,53,1,53,1,53,1,53,1,53,1,53,3,53,834,8, - 53,1,53,1,53,1,53,1,53,1,53,1,53,1,53,1,53,3,53,844,8,53,1,54,1, - 54,1,54,1,54,1,54,1,54,1,54,3,54,853,8,54,1,54,1,54,1,55,1,55,1, - 55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,3,55,871, - 8,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55, - 1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,5,55,893,8,55,10,55,12,55, - 896,9,55,1,55,1,55,1,55,3,55,901,8,55,3,55,903,8,55,1,56,1,56,1, - 56,1,56,1,56,1,57,1,57,1,58,1,58,1,59,1,59,1,59,1,59,1,59,1,59,1, - 59,1,59,3,59,922,8,59,1,60,1,60,1,60,1,60,5,60,928,8,60,10,60,12, - 60,931,9,60,3,60,933,8,60,1,60,1,60,1,61,1,61,1,61,1,61,1,62,1,62, - 1,62,1,62,5,62,945,8,62,10,62,12,62,948,9,62,3,62,950,8,62,1,62, - 1,62,1,63,1,63,1,64,1,64,1,64,0,0,65,0,2,4,6,8,10,12,14,16,18,20, + 59,7,59,2,60,7,60,2,61,7,61,2,62,7,62,2,63,7,63,2,64,7,64,2,65,7, + 65,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,3,0,145,8,0,1, + 1,1,1,1,1,5,1,150,8,1,10,1,12,1,153,9,1,1,1,1,1,1,2,1,2,1,2,1,2, + 1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,5,3,171,8,3,10,3,12,3,174, + 9,3,1,3,1,3,1,4,1,4,1,4,1,4,1,4,1,4,1,4,3,4,185,8,4,1,5,1,5,1,5, + 1,5,1,5,1,5,1,5,1,5,1,5,1,5,3,5,197,8,5,1,6,1,6,1,6,1,6,1,6,1,6, + 1,6,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,8,1,8,1,8,1,8,3,8,217,8,8,1,9, + 1,9,1,9,1,9,1,9,1,9,3,9,225,8,9,1,9,1,9,1,10,5,10,230,8,10,10,10, + 12,10,233,9,10,1,10,1,10,1,10,3,10,238,8,10,1,10,1,10,1,10,1,10, + 1,10,1,10,1,10,1,10,5,10,248,8,10,10,10,12,10,251,9,10,3,10,253, + 8,10,1,10,1,10,5,10,257,8,10,10,10,12,10,260,9,10,1,10,1,10,1,11, + 1,11,1,11,1,11,5,11,268,8,11,10,11,12,11,271,9,11,1,11,1,11,1,12, + 1,12,1,12,1,12,3,12,279,8,12,1,12,1,12,1,12,1,12,1,12,1,12,5,12, + 287,8,12,10,12,12,12,290,9,12,3,12,292,8,12,3,12,294,8,12,1,13,1, + 13,3,13,298,8,13,1,14,1,14,1,14,1,14,5,14,304,8,14,10,14,12,14,307, + 9,14,1,14,1,14,1,15,1,15,1,15,1,15,1,15,1,15,1,15,3,15,318,8,15, + 1,16,1,16,1,16,3,16,323,8,16,1,16,1,16,1,16,1,16,1,17,1,17,1,17, + 3,17,332,8,17,1,18,3,18,335,8,18,1,18,1,18,1,18,1,18,1,18,1,18,1, + 18,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,19,1,19,1,19,1,19,1, + 19,1,19,1,19,1,19,5,19,360,8,19,10,19,12,19,363,9,19,1,19,1,19,1, + 20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1, + 20,1,20,1,20,1,20,1,20,1,20,3,20,386,8,20,1,21,1,21,1,21,1,21,1, + 21,1,21,1,21,1,21,3,21,396,8,21,1,21,1,21,5,21,400,8,21,10,21,12, + 21,403,9,21,1,21,1,21,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1, + 22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1, + 22,1,22,1,22,3,22,431,8,22,1,23,1,23,1,23,1,23,1,23,3,23,438,8,23, + 1,23,1,23,3,23,442,8,23,1,24,5,24,445,8,24,10,24,12,24,448,9,24, + 1,24,1,24,1,24,1,24,1,24,1,24,1,24,1,24,3,24,458,8,24,1,24,1,24, + 5,24,462,8,24,10,24,12,24,465,9,24,1,24,1,24,1,25,1,25,1,25,3,25, + 472,8,25,1,26,1,26,1,26,3,26,477,8,26,1,26,1,26,1,26,1,26,1,26,1, + 26,1,26,1,26,1,26,3,26,488,8,26,1,26,1,26,1,26,3,26,493,8,26,1,26, + 1,26,1,27,1,27,1,27,3,27,500,8,27,1,27,1,27,1,27,1,27,1,27,1,27, + 1,27,3,27,509,8,27,1,27,1,27,1,28,1,28,1,28,1,28,1,28,1,28,1,28, + 1,28,1,28,3,28,522,8,28,1,28,1,28,1,29,1,29,1,29,1,30,1,30,1,31, + 1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,5,31,541,8,31,10,31, + 12,31,544,9,31,3,31,546,8,31,1,31,3,31,549,8,31,1,32,1,32,1,32,5, + 32,554,8,32,10,32,12,32,557,9,32,1,33,1,33,1,33,5,33,562,8,33,10, + 33,12,33,565,9,33,1,33,1,33,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1, + 34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1, + 34,1,34,1,34,1,34,1,34,1,34,3,34,595,8,34,1,34,1,34,1,34,1,34,1, + 34,1,34,1,34,1,34,1,34,1,34,3,34,607,8,34,1,34,1,34,3,34,611,8,34, + 1,35,1,35,1,35,1,35,5,35,617,8,35,10,35,12,35,620,9,35,1,35,1,35, + 1,36,1,36,1,37,1,37,1,37,1,38,1,38,3,38,631,8,38,1,39,1,39,1,39, + 1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,1,39,3,39,645,8,39,1,39, + 1,39,1,40,1,40,1,40,1,40,1,40,1,40,3,40,655,8,40,1,41,1,41,1,41, + 1,41,1,41,1,41,1,41,1,41,1,41,1,42,1,42,1,42,1,42,1,42,1,42,1,42, + 3,42,673,8,42,1,42,1,42,3,42,677,8,42,1,42,3,42,680,8,42,1,42,1, + 42,3,42,684,8,42,1,42,3,42,687,8,42,1,42,1,42,1,43,1,43,1,43,1,43, + 1,43,3,43,696,8,43,1,43,1,43,3,43,700,8,43,1,43,1,43,3,43,704,8, + 43,1,43,1,43,5,43,708,8,43,10,43,12,43,711,9,43,1,43,1,43,1,44,1, + 44,1,44,1,44,1,44,3,44,720,8,44,1,45,1,45,1,45,1,45,1,45,1,45,1, + 45,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1, + 46,1,46,1,47,1,47,1,47,1,47,1,47,1,47,1,48,1,48,1,48,1,48,1,49,1, + 49,1,49,1,49,1,49,1,49,1,49,1,49,1,49,1,49,1,49,3,49,764,8,49,1, + 50,1,50,1,50,1,50,1,50,1,50,1,50,1,50,1,50,1,50,1,50,1,50,1,50,1, + 50,1,50,1,50,1,50,3,50,783,8,50,1,50,3,50,786,8,50,3,50,788,8,50, + 1,51,1,51,1,52,1,52,1,53,1,53,1,53,5,53,797,8,53,10,53,12,53,800, + 9,53,1,53,1,53,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54, + 3,54,814,8,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,1,54, + 1,54,1,54,1,54,1,54,3,54,830,8,54,1,54,1,54,1,54,1,54,1,54,1,54, + 1,54,3,54,839,8,54,1,54,1,54,1,54,1,54,1,54,1,54,3,54,847,8,54,1, + 54,1,54,1,54,1,54,1,54,1,54,1,54,1,54,3,54,857,8,54,1,55,1,55,1, + 55,1,55,1,55,1,55,1,55,3,55,866,8,55,1,55,1,55,1,56,1,56,1,56,1, + 56,1,56,1,56,1,56,1,56,1,56,1,56,1,56,1,56,1,56,1,56,3,56,884,8, + 56,1,56,1,56,1,56,1,56,1,56,1,56,1,56,1,56,1,56,1,56,1,56,1,56,1, + 56,1,56,1,56,1,56,1,56,1,56,1,56,1,56,5,56,906,8,56,10,56,12,56, + 909,9,56,1,56,1,56,1,56,3,56,914,8,56,3,56,916,8,56,1,57,1,57,1, + 57,1,57,1,57,1,58,1,58,1,59,1,59,1,60,1,60,1,60,1,60,1,60,1,60,1, + 60,1,60,3,60,935,8,60,1,61,1,61,1,61,1,61,5,61,941,8,61,10,61,12, + 61,944,9,61,3,61,946,8,61,1,61,1,61,1,62,1,62,1,62,1,62,1,63,1,63, + 1,63,1,63,5,63,958,8,63,10,63,12,63,961,9,63,3,63,963,8,63,1,63, + 1,63,1,64,1,64,1,65,1,65,1,65,0,0,66,0,2,4,6,8,10,12,14,16,18,20, 22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64, 66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106, - 108,110,112,114,116,118,120,122,124,126,128,0,7,1,0,65,69,2,0,60, - 64,66,67,2,0,60,61,66,67,2,0,62,64,66,67,1,0,85,92,1,0,72,75,2,0, - 39,39,113,113,1022,0,142,1,0,0,0,2,144,1,0,0,0,4,154,1,0,0,0,6,158, - 1,0,0,0,8,182,1,0,0,0,10,194,1,0,0,0,12,196,1,0,0,0,14,203,1,0,0, - 0,16,214,1,0,0,0,18,216,1,0,0,0,20,229,1,0,0,0,22,261,1,0,0,0,24, - 291,1,0,0,0,26,293,1,0,0,0,28,297,1,0,0,0,30,315,1,0,0,0,32,317, - 1,0,0,0,34,329,1,0,0,0,36,331,1,0,0,0,38,346,1,0,0,0,40,380,1,0, - 0,0,42,382,1,0,0,0,44,425,1,0,0,0,46,436,1,0,0,0,48,441,1,0,0,0, - 50,466,1,0,0,0,52,468,1,0,0,0,54,491,1,0,0,0,56,507,1,0,0,0,58,520, - 1,0,0,0,60,523,1,0,0,0,62,543,1,0,0,0,64,545,1,0,0,0,66,553,1,0, - 0,0,68,605,1,0,0,0,70,607,1,0,0,0,72,618,1,0,0,0,74,620,1,0,0,0, - 76,625,1,0,0,0,78,627,1,0,0,0,80,649,1,0,0,0,82,651,1,0,0,0,84,660, - 1,0,0,0,86,685,1,0,0,0,88,713,1,0,0,0,90,715,1,0,0,0,92,729,1,0, - 0,0,94,735,1,0,0,0,96,750,1,0,0,0,98,774,1,0,0,0,100,776,1,0,0,0, - 102,778,1,0,0,0,104,780,1,0,0,0,106,843,1,0,0,0,108,845,1,0,0,0, - 110,902,1,0,0,0,112,904,1,0,0,0,114,909,1,0,0,0,116,911,1,0,0,0, - 118,921,1,0,0,0,120,923,1,0,0,0,122,936,1,0,0,0,124,940,1,0,0,0, - 126,953,1,0,0,0,128,955,1,0,0,0,130,131,3,6,3,0,131,132,5,0,0,1, - 132,143,1,0,0,0,133,134,3,20,10,0,134,135,5,0,0,1,135,143,1,0,0, - 0,136,137,3,48,24,0,137,138,5,0,0,1,138,143,1,0,0,0,139,140,3,2, - 1,0,140,141,5,0,0,1,141,143,1,0,0,0,142,130,1,0,0,0,142,133,1,0, - 0,0,142,136,1,0,0,0,142,139,1,0,0,0,143,1,1,0,0,0,144,145,5,7,0, - 0,145,149,5,101,0,0,146,148,3,8,4,0,147,146,1,0,0,0,148,151,1,0, - 0,0,149,147,1,0,0,0,149,150,1,0,0,0,150,152,1,0,0,0,151,149,1,0, - 0,0,152,153,5,102,0,0,153,3,1,0,0,0,154,155,5,8,0,0,155,156,3,128, - 64,0,156,157,5,98,0,0,157,5,1,0,0,0,158,159,5,1,0,0,159,160,3,126, - 63,0,160,161,5,48,0,0,161,162,3,128,64,0,162,163,5,42,0,0,163,164, - 3,128,64,0,164,165,5,41,0,0,165,166,3,128,64,0,166,170,5,101,0,0, - 167,169,3,8,4,0,168,167,1,0,0,0,169,172,1,0,0,0,170,168,1,0,0,0, - 170,171,1,0,0,0,171,173,1,0,0,0,172,170,1,0,0,0,173,174,5,102,0, - 0,174,7,1,0,0,0,175,183,3,4,2,0,176,183,3,18,9,0,177,183,3,10,5, - 0,178,183,3,74,37,0,179,183,3,86,43,0,180,183,3,108,54,0,181,183, - 3,32,16,0,182,175,1,0,0,0,182,176,1,0,0,0,182,177,1,0,0,0,182,178, - 1,0,0,0,182,179,1,0,0,0,182,180,1,0,0,0,182,181,1,0,0,0,183,9,1, - 0,0,0,184,185,5,8,0,0,185,186,5,11,0,0,186,187,3,126,63,0,187,188, - 5,98,0,0,188,195,1,0,0,0,189,190,5,8,0,0,190,191,5,13,0,0,191,192, - 3,126,63,0,192,193,5,98,0,0,193,195,1,0,0,0,194,184,1,0,0,0,194, - 189,1,0,0,0,195,11,1,0,0,0,196,197,5,9,0,0,197,198,5,10,0,0,198, - 199,3,126,63,0,199,200,5,48,0,0,200,201,3,128,64,0,201,202,5,98, - 0,0,202,13,1,0,0,0,203,204,5,9,0,0,204,205,5,11,0,0,205,206,3,126, - 63,0,206,207,5,42,0,0,207,208,3,128,64,0,208,209,5,98,0,0,209,15, - 1,0,0,0,210,215,3,10,5,0,211,215,3,12,6,0,212,215,3,14,7,0,213,215, - 3,32,16,0,214,210,1,0,0,0,214,211,1,0,0,0,214,212,1,0,0,0,214,213, - 1,0,0,0,215,17,1,0,0,0,216,217,5,10,0,0,217,218,3,126,63,0,218,219, - 5,48,0,0,219,222,3,128,64,0,220,221,5,49,0,0,221,223,3,128,64,0, - 222,220,1,0,0,0,222,223,1,0,0,0,223,224,1,0,0,0,224,225,5,98,0,0, - 225,19,1,0,0,0,226,228,3,16,8,0,227,226,1,0,0,0,228,231,1,0,0,0, - 229,227,1,0,0,0,229,230,1,0,0,0,230,232,1,0,0,0,231,229,1,0,0,0, - 232,233,5,11,0,0,233,235,3,126,63,0,234,236,3,22,11,0,235,234,1, - 0,0,0,235,236,1,0,0,0,236,237,1,0,0,0,237,238,5,48,0,0,238,239,3, - 128,64,0,239,240,5,42,0,0,240,250,3,128,64,0,241,242,5,53,0,0,242, - 247,3,26,13,0,243,244,5,99,0,0,244,246,3,26,13,0,245,243,1,0,0,0, - 246,249,1,0,0,0,247,245,1,0,0,0,247,248,1,0,0,0,248,251,1,0,0,0, - 249,247,1,0,0,0,250,241,1,0,0,0,250,251,1,0,0,0,251,252,1,0,0,0, - 252,256,5,101,0,0,253,255,3,34,17,0,254,253,1,0,0,0,255,258,1,0, - 0,0,256,254,1,0,0,0,256,257,1,0,0,0,257,259,1,0,0,0,258,256,1,0, - 0,0,259,260,5,102,0,0,260,21,1,0,0,0,261,262,5,107,0,0,262,267,3, - 24,12,0,263,264,5,99,0,0,264,266,3,24,12,0,265,263,1,0,0,0,266,269, - 1,0,0,0,267,265,1,0,0,0,267,268,1,0,0,0,268,270,1,0,0,0,269,267, - 1,0,0,0,270,271,5,108,0,0,271,23,1,0,0,0,272,273,5,14,0,0,273,276, - 3,126,63,0,274,275,5,97,0,0,275,277,5,4,0,0,276,274,1,0,0,0,276, - 277,1,0,0,0,277,292,1,0,0,0,278,279,5,3,0,0,279,289,3,126,63,0,280, - 281,5,5,0,0,281,286,3,26,13,0,282,283,5,109,0,0,283,285,3,26,13, - 0,284,282,1,0,0,0,285,288,1,0,0,0,286,284,1,0,0,0,286,287,1,0,0, - 0,287,290,1,0,0,0,288,286,1,0,0,0,289,280,1,0,0,0,289,290,1,0,0, - 0,290,292,1,0,0,0,291,272,1,0,0,0,291,278,1,0,0,0,292,25,1,0,0,0, - 293,295,3,126,63,0,294,296,3,28,14,0,295,294,1,0,0,0,295,296,1,0, - 0,0,296,27,1,0,0,0,297,298,5,107,0,0,298,303,3,30,15,0,299,300,5, - 99,0,0,300,302,3,30,15,0,301,299,1,0,0,0,302,305,1,0,0,0,303,301, - 1,0,0,0,303,304,1,0,0,0,304,306,1,0,0,0,305,303,1,0,0,0,306,307, - 5,108,0,0,307,29,1,0,0,0,308,309,5,10,0,0,309,316,3,126,63,0,310, - 311,5,11,0,0,311,316,3,26,13,0,312,313,5,3,0,0,313,316,3,126,63, - 0,314,316,3,110,55,0,315,308,1,0,0,0,315,310,1,0,0,0,315,312,1,0, - 0,0,315,314,1,0,0,0,316,31,1,0,0,0,317,318,5,2,0,0,318,320,3,126, - 63,0,319,321,3,22,11,0,320,319,1,0,0,0,320,321,1,0,0,0,321,322,1, - 0,0,0,322,323,5,110,0,0,323,324,3,110,55,0,324,325,5,98,0,0,325, - 33,1,0,0,0,326,330,3,38,19,0,327,330,3,42,21,0,328,330,3,36,18,0, - 329,326,1,0,0,0,329,327,1,0,0,0,329,328,1,0,0,0,330,35,1,0,0,0,331, - 332,5,16,0,0,332,333,3,126,63,0,333,334,5,48,0,0,334,335,3,128,64, - 0,335,336,5,97,0,0,336,337,3,110,55,0,337,338,5,96,0,0,338,339,3, - 110,55,0,339,340,5,101,0,0,340,341,5,65,0,0,341,342,5,48,0,0,342, - 343,3,128,64,0,343,344,5,98,0,0,344,345,5,102,0,0,345,37,1,0,0,0, - 346,347,5,14,0,0,347,348,3,126,63,0,348,349,5,48,0,0,349,350,3,128, - 64,0,350,351,5,97,0,0,351,352,3,110,55,0,352,356,5,101,0,0,353,355, - 3,40,20,0,354,353,1,0,0,0,355,358,1,0,0,0,356,354,1,0,0,0,356,357, - 1,0,0,0,357,359,1,0,0,0,358,356,1,0,0,0,359,360,5,102,0,0,360,39, - 1,0,0,0,361,362,5,55,0,0,362,363,5,48,0,0,363,364,3,128,64,0,364, - 365,5,98,0,0,365,381,1,0,0,0,366,367,5,56,0,0,367,368,5,48,0,0,368, - 369,3,128,64,0,369,370,5,98,0,0,370,381,1,0,0,0,371,372,5,57,0,0, - 372,373,5,58,0,0,373,374,5,48,0,0,374,375,3,128,64,0,375,376,5,59, - 0,0,376,377,5,48,0,0,377,378,3,128,64,0,378,379,5,98,0,0,379,381, - 1,0,0,0,380,361,1,0,0,0,380,366,1,0,0,0,380,371,1,0,0,0,381,41,1, - 0,0,0,382,383,5,15,0,0,383,384,3,126,63,0,384,385,5,48,0,0,385,386, - 3,128,64,0,386,387,5,97,0,0,387,388,3,116,58,0,388,390,3,46,23,0, - 389,391,5,76,0,0,390,389,1,0,0,0,390,391,1,0,0,0,391,392,1,0,0,0, - 392,396,5,101,0,0,393,395,3,44,22,0,394,393,1,0,0,0,395,398,1,0, - 0,0,396,394,1,0,0,0,396,397,1,0,0,0,397,399,1,0,0,0,398,396,1,0, - 0,0,399,400,5,102,0,0,400,43,1,0,0,0,401,402,5,62,0,0,402,403,5, - 48,0,0,403,404,3,128,64,0,404,405,5,98,0,0,405,426,1,0,0,0,406,407, - 5,63,0,0,407,408,5,48,0,0,408,409,3,128,64,0,409,410,5,98,0,0,410, - 426,1,0,0,0,411,412,5,64,0,0,412,413,5,48,0,0,413,414,3,128,64,0, - 414,415,5,98,0,0,415,426,1,0,0,0,416,417,5,57,0,0,417,418,5,58,0, - 0,418,419,5,48,0,0,419,420,3,128,64,0,420,421,5,59,0,0,421,422,5, - 48,0,0,422,423,3,128,64,0,423,424,5,98,0,0,424,426,1,0,0,0,425,401, - 1,0,0,0,425,406,1,0,0,0,425,411,1,0,0,0,425,416,1,0,0,0,426,45,1, - 0,0,0,427,428,5,10,0,0,428,437,3,126,63,0,429,430,5,11,0,0,430,432, - 3,126,63,0,431,433,3,28,14,0,432,431,1,0,0,0,432,433,1,0,0,0,433, - 437,1,0,0,0,434,435,5,3,0,0,435,437,3,126,63,0,436,427,1,0,0,0,436, - 429,1,0,0,0,436,434,1,0,0,0,437,47,1,0,0,0,438,440,3,16,8,0,439, - 438,1,0,0,0,440,443,1,0,0,0,441,439,1,0,0,0,441,442,1,0,0,0,442, - 444,1,0,0,0,443,441,1,0,0,0,444,445,5,13,0,0,445,446,3,126,63,0, - 446,447,5,48,0,0,447,448,3,128,64,0,448,449,5,42,0,0,449,452,3,128, - 64,0,450,451,5,43,0,0,451,453,5,111,0,0,452,450,1,0,0,0,452,453, - 1,0,0,0,453,454,1,0,0,0,454,458,5,101,0,0,455,457,3,50,25,0,456, - 455,1,0,0,0,457,460,1,0,0,0,458,456,1,0,0,0,458,459,1,0,0,0,459, - 461,1,0,0,0,460,458,1,0,0,0,461,462,5,102,0,0,462,49,1,0,0,0,463, - 467,3,52,26,0,464,467,3,54,27,0,465,467,3,56,28,0,466,463,1,0,0, - 0,466,464,1,0,0,0,466,465,1,0,0,0,467,51,1,0,0,0,468,469,5,16,0, - 0,469,471,3,126,63,0,470,472,3,22,11,0,471,470,1,0,0,0,471,472,1, - 0,0,0,472,473,1,0,0,0,473,474,5,48,0,0,474,475,3,128,64,0,475,476, - 5,97,0,0,476,477,3,110,55,0,477,478,5,96,0,0,478,479,3,110,55,0, - 479,480,5,50,0,0,480,482,3,60,30,0,481,483,3,58,29,0,482,481,1,0, - 0,0,482,483,1,0,0,0,483,484,1,0,0,0,484,485,5,52,0,0,485,487,3,62, - 31,0,486,488,3,66,33,0,487,486,1,0,0,0,487,488,1,0,0,0,488,489,1, - 0,0,0,489,490,5,98,0,0,490,53,1,0,0,0,491,492,5,17,0,0,492,494,3, - 126,63,0,493,495,3,22,11,0,494,493,1,0,0,0,494,495,1,0,0,0,495,496, - 1,0,0,0,496,497,5,48,0,0,497,498,3,128,64,0,498,499,5,97,0,0,499, - 500,3,110,55,0,500,501,5,96,0,0,501,503,3,110,55,0,502,504,3,66, - 33,0,503,502,1,0,0,0,503,504,1,0,0,0,504,505,1,0,0,0,505,506,5,98, - 0,0,506,55,1,0,0,0,507,508,5,18,0,0,508,509,3,126,63,0,509,510,5, - 48,0,0,510,511,3,128,64,0,511,512,5,19,0,0,512,513,3,126,63,0,513, - 514,5,97,0,0,514,516,3,110,55,0,515,517,3,66,33,0,516,515,1,0,0, - 0,516,517,1,0,0,0,517,518,1,0,0,0,518,519,5,98,0,0,519,57,1,0,0, - 0,520,521,5,51,0,0,521,522,3,110,55,0,522,59,1,0,0,0,523,524,7,0, - 0,0,524,61,1,0,0,0,525,544,5,54,0,0,526,527,5,10,0,0,527,544,3,126, - 63,0,528,529,5,3,0,0,529,544,3,126,63,0,530,531,5,12,0,0,531,540, - 5,103,0,0,532,537,3,26,13,0,533,534,5,99,0,0,534,536,3,26,13,0,535, - 533,1,0,0,0,536,539,1,0,0,0,537,535,1,0,0,0,537,538,1,0,0,0,538, - 541,1,0,0,0,539,537,1,0,0,0,540,532,1,0,0,0,540,541,1,0,0,0,541, - 542,1,0,0,0,542,544,5,104,0,0,543,525,1,0,0,0,543,526,1,0,0,0,543, - 528,1,0,0,0,543,530,1,0,0,0,544,63,1,0,0,0,545,550,3,126,63,0,546, - 547,5,99,0,0,547,549,3,126,63,0,548,546,1,0,0,0,549,552,1,0,0,0, - 550,548,1,0,0,0,550,551,1,0,0,0,551,65,1,0,0,0,552,550,1,0,0,0,553, - 554,5,53,0,0,554,558,5,101,0,0,555,557,3,68,34,0,556,555,1,0,0,0, - 557,560,1,0,0,0,558,556,1,0,0,0,558,559,1,0,0,0,559,561,1,0,0,0, - 560,558,1,0,0,0,561,562,5,102,0,0,562,67,1,0,0,0,563,564,5,27,0, - 0,564,565,3,126,63,0,565,566,5,48,0,0,566,567,3,128,64,0,567,568, - 5,97,0,0,568,569,3,110,55,0,569,570,3,70,35,0,570,571,5,98,0,0,571, - 606,1,0,0,0,572,573,5,28,0,0,573,574,3,126,63,0,574,575,5,48,0,0, - 575,576,3,128,64,0,576,577,5,97,0,0,577,578,3,116,58,0,578,579,3, - 46,23,0,579,580,3,70,35,0,580,581,5,98,0,0,581,606,1,0,0,0,582,583, - 5,11,0,0,583,584,3,126,63,0,584,585,5,48,0,0,585,586,3,128,64,0, - 586,587,5,97,0,0,587,589,3,126,63,0,588,590,3,28,14,0,589,588,1, - 0,0,0,589,590,1,0,0,0,590,591,1,0,0,0,591,592,5,98,0,0,592,606,1, - 0,0,0,593,594,5,18,0,0,594,595,3,126,63,0,595,596,5,48,0,0,596,597, - 3,128,64,0,597,598,5,97,0,0,598,601,3,126,63,0,599,600,5,20,0,0, - 600,602,3,110,55,0,601,599,1,0,0,0,601,602,1,0,0,0,602,603,1,0,0, - 0,603,604,5,98,0,0,604,606,1,0,0,0,605,563,1,0,0,0,605,572,1,0,0, - 0,605,582,1,0,0,0,605,593,1,0,0,0,606,69,1,0,0,0,607,608,5,103,0, - 0,608,613,3,72,36,0,609,610,5,99,0,0,610,612,3,72,36,0,611,609,1, - 0,0,0,612,615,1,0,0,0,613,611,1,0,0,0,613,614,1,0,0,0,614,616,1, - 0,0,0,615,613,1,0,0,0,616,617,5,104,0,0,617,71,1,0,0,0,618,619,7, - 1,0,0,619,73,1,0,0,0,620,621,5,26,0,0,621,622,3,76,38,0,622,75,1, - 0,0,0,623,626,3,78,39,0,624,626,3,82,41,0,625,623,1,0,0,0,625,624, - 1,0,0,0,626,77,1,0,0,0,627,628,5,27,0,0,628,629,3,126,63,0,629,630, - 5,48,0,0,630,631,3,128,64,0,631,632,5,36,0,0,632,633,3,126,63,0, - 633,634,5,97,0,0,634,635,3,110,55,0,635,636,5,37,0,0,636,639,3,80, - 40,0,637,638,5,38,0,0,638,640,3,118,59,0,639,637,1,0,0,0,639,640, - 1,0,0,0,640,641,1,0,0,0,641,642,5,98,0,0,642,79,1,0,0,0,643,650, - 5,70,0,0,644,645,5,71,0,0,645,646,5,105,0,0,646,647,3,110,55,0,647, - 648,5,106,0,0,648,650,1,0,0,0,649,643,1,0,0,0,649,644,1,0,0,0,650, - 81,1,0,0,0,651,652,5,28,0,0,652,653,3,126,63,0,653,654,5,48,0,0, - 654,655,3,128,64,0,655,656,5,101,0,0,656,657,3,84,42,0,657,658,3, - 84,42,0,658,659,5,102,0,0,659,83,1,0,0,0,660,661,3,46,23,0,661,662, - 5,29,0,0,662,663,3,126,63,0,663,664,5,48,0,0,664,665,3,128,64,0, - 665,667,3,116,58,0,666,668,5,76,0,0,667,666,1,0,0,0,667,668,1,0, - 0,0,668,671,1,0,0,0,669,670,5,44,0,0,670,672,3,128,64,0,671,669, - 1,0,0,0,671,672,1,0,0,0,672,674,1,0,0,0,673,675,5,45,0,0,674,673, - 1,0,0,0,674,675,1,0,0,0,675,678,1,0,0,0,676,677,5,46,0,0,677,679, - 3,128,64,0,678,676,1,0,0,0,678,679,1,0,0,0,679,681,1,0,0,0,680,682, - 5,47,0,0,681,680,1,0,0,0,681,682,1,0,0,0,682,683,1,0,0,0,683,684, - 5,98,0,0,684,85,1,0,0,0,685,686,5,21,0,0,686,687,3,126,63,0,687, - 688,5,22,0,0,688,690,3,126,63,0,689,691,3,28,14,0,690,689,1,0,0, - 0,690,691,1,0,0,0,691,694,1,0,0,0,692,693,5,48,0,0,693,695,3,128, - 64,0,694,692,1,0,0,0,694,695,1,0,0,0,695,698,1,0,0,0,696,697,5,43, - 0,0,697,699,5,111,0,0,698,696,1,0,0,0,698,699,1,0,0,0,699,700,1, - 0,0,0,700,704,5,101,0,0,701,703,3,88,44,0,702,701,1,0,0,0,703,706, - 1,0,0,0,704,702,1,0,0,0,704,705,1,0,0,0,705,707,1,0,0,0,706,704, - 1,0,0,0,707,708,5,102,0,0,708,87,1,0,0,0,709,710,5,25,0,0,710,714, - 3,76,38,0,711,714,3,92,46,0,712,714,3,90,45,0,713,709,1,0,0,0,713, - 711,1,0,0,0,713,712,1,0,0,0,714,89,1,0,0,0,715,716,5,33,0,0,716, - 717,3,126,63,0,717,718,5,34,0,0,718,719,5,35,0,0,719,720,5,31,0, - 0,720,721,5,18,0,0,721,722,3,126,63,0,722,723,5,32,0,0,723,724,5, - 28,0,0,724,725,3,126,63,0,725,726,5,100,0,0,726,727,3,126,63,0,727, - 728,5,98,0,0,728,91,1,0,0,0,729,730,5,23,0,0,730,731,3,94,47,0,731, - 732,5,24,0,0,732,733,3,98,49,0,733,734,5,98,0,0,734,93,1,0,0,0,735, - 736,3,126,63,0,736,737,5,100,0,0,737,738,3,96,48,0,738,95,1,0,0, - 0,739,751,3,126,63,0,740,751,5,65,0,0,741,751,5,55,0,0,742,751,5, - 56,0,0,743,751,5,62,0,0,744,751,5,63,0,0,745,751,5,64,0,0,746,751, - 5,66,0,0,747,751,5,67,0,0,748,751,5,68,0,0,749,751,5,69,0,0,750, - 739,1,0,0,0,750,740,1,0,0,0,750,741,1,0,0,0,750,742,1,0,0,0,750, - 743,1,0,0,0,750,744,1,0,0,0,750,745,1,0,0,0,750,746,1,0,0,0,750, - 747,1,0,0,0,750,748,1,0,0,0,750,749,1,0,0,0,751,97,1,0,0,0,752,753, - 5,27,0,0,753,754,3,126,63,0,754,755,5,100,0,0,755,756,3,100,50,0, - 756,775,1,0,0,0,757,758,5,28,0,0,758,759,3,126,63,0,759,760,5,100, - 0,0,760,761,3,126,63,0,761,762,5,100,0,0,762,763,3,102,51,0,763, - 775,1,0,0,0,764,765,5,13,0,0,765,766,3,126,63,0,766,767,5,100,0, - 0,767,769,3,126,63,0,768,770,3,28,14,0,769,768,1,0,0,0,769,770,1, - 0,0,0,770,772,1,0,0,0,771,773,3,104,52,0,772,771,1,0,0,0,772,773, - 1,0,0,0,773,775,1,0,0,0,774,752,1,0,0,0,774,757,1,0,0,0,774,764, - 1,0,0,0,775,99,1,0,0,0,776,777,7,2,0,0,777,101,1,0,0,0,778,779,7, - 3,0,0,779,103,1,0,0,0,780,781,5,30,0,0,781,785,5,101,0,0,782,784, - 3,106,53,0,783,782,1,0,0,0,784,787,1,0,0,0,785,783,1,0,0,0,785,786, - 1,0,0,0,786,788,1,0,0,0,787,785,1,0,0,0,788,789,5,102,0,0,789,105, - 1,0,0,0,790,791,3,126,63,0,791,792,5,24,0,0,792,793,5,27,0,0,793, - 800,3,126,63,0,794,795,5,32,0,0,795,796,5,28,0,0,796,797,3,126,63, - 0,797,798,5,100,0,0,798,799,3,126,63,0,799,801,1,0,0,0,800,794,1, - 0,0,0,800,801,1,0,0,0,801,802,1,0,0,0,802,803,5,98,0,0,803,844,1, - 0,0,0,804,805,3,126,63,0,805,806,5,24,0,0,806,807,5,28,0,0,807,808, - 3,126,63,0,808,809,5,100,0,0,809,816,3,126,63,0,810,811,5,32,0,0, - 811,812,5,28,0,0,812,813,3,126,63,0,813,814,5,100,0,0,814,815,3, - 126,63,0,815,817,1,0,0,0,816,810,1,0,0,0,816,817,1,0,0,0,817,818, - 1,0,0,0,818,819,5,98,0,0,819,844,1,0,0,0,820,821,3,126,63,0,821, - 822,5,24,0,0,822,823,5,11,0,0,823,825,3,126,63,0,824,826,3,28,14, - 0,825,824,1,0,0,0,825,826,1,0,0,0,826,833,1,0,0,0,827,828,5,32,0, - 0,828,829,5,28,0,0,829,830,3,126,63,0,830,831,5,100,0,0,831,832, - 3,126,63,0,832,834,1,0,0,0,833,827,1,0,0,0,833,834,1,0,0,0,834,835, - 1,0,0,0,835,836,5,98,0,0,836,844,1,0,0,0,837,838,3,126,63,0,838, - 839,5,24,0,0,839,840,5,18,0,0,840,841,3,126,63,0,841,842,5,98,0, - 0,842,844,1,0,0,0,843,790,1,0,0,0,843,804,1,0,0,0,843,820,1,0,0, - 0,843,837,1,0,0,0,844,107,1,0,0,0,845,846,5,18,0,0,846,847,3,126, - 63,0,847,848,5,24,0,0,848,849,3,126,63,0,849,850,5,100,0,0,850,852, - 3,126,63,0,851,853,3,104,52,0,852,851,1,0,0,0,852,853,1,0,0,0,853, - 854,1,0,0,0,854,855,5,98,0,0,855,109,1,0,0,0,856,903,3,114,57,0, - 857,903,5,77,0,0,858,903,5,78,0,0,859,860,5,79,0,0,860,903,3,128, - 64,0,861,862,5,80,0,0,862,863,5,107,0,0,863,864,3,126,63,0,864,865, - 5,108,0,0,865,903,1,0,0,0,866,867,5,81,0,0,867,868,5,107,0,0,868, - 870,3,126,63,0,869,871,3,28,14,0,870,869,1,0,0,0,870,871,1,0,0,0, - 871,872,1,0,0,0,872,873,5,108,0,0,873,903,1,0,0,0,874,875,5,6,0, - 0,875,876,5,107,0,0,876,877,3,126,63,0,877,878,5,108,0,0,878,903, - 1,0,0,0,879,880,5,82,0,0,880,881,5,107,0,0,881,882,3,110,55,0,882, - 883,5,108,0,0,883,903,1,0,0,0,884,885,5,83,0,0,885,886,5,107,0,0, - 886,887,3,110,55,0,887,888,5,108,0,0,888,903,1,0,0,0,889,890,5,84, - 0,0,890,894,5,101,0,0,891,893,3,112,56,0,892,891,1,0,0,0,893,896, - 1,0,0,0,894,892,1,0,0,0,894,895,1,0,0,0,895,897,1,0,0,0,896,894, - 1,0,0,0,897,903,5,102,0,0,898,900,3,126,63,0,899,901,3,28,14,0,900, - 899,1,0,0,0,900,901,1,0,0,0,901,903,1,0,0,0,902,856,1,0,0,0,902, - 857,1,0,0,0,902,858,1,0,0,0,902,859,1,0,0,0,902,861,1,0,0,0,902, - 866,1,0,0,0,902,874,1,0,0,0,902,879,1,0,0,0,902,884,1,0,0,0,902, - 889,1,0,0,0,902,898,1,0,0,0,903,111,1,0,0,0,904,905,3,126,63,0,905, - 906,5,97,0,0,906,907,3,110,55,0,907,908,5,98,0,0,908,113,1,0,0,0, - 909,910,7,4,0,0,910,115,1,0,0,0,911,912,7,5,0,0,912,117,1,0,0,0, - 913,922,3,128,64,0,914,922,5,111,0,0,915,922,5,112,0,0,916,922,5, - 93,0,0,917,922,5,94,0,0,918,922,5,95,0,0,919,922,3,120,60,0,920, - 922,3,124,62,0,921,913,1,0,0,0,921,914,1,0,0,0,921,915,1,0,0,0,921, - 916,1,0,0,0,921,917,1,0,0,0,921,918,1,0,0,0,921,919,1,0,0,0,921, - 920,1,0,0,0,922,119,1,0,0,0,923,932,5,101,0,0,924,929,3,122,61,0, - 925,926,5,99,0,0,926,928,3,122,61,0,927,925,1,0,0,0,928,931,1,0, - 0,0,929,927,1,0,0,0,929,930,1,0,0,0,930,933,1,0,0,0,931,929,1,0, - 0,0,932,924,1,0,0,0,932,933,1,0,0,0,933,934,1,0,0,0,934,935,5,102, - 0,0,935,121,1,0,0,0,936,937,3,128,64,0,937,938,5,97,0,0,938,939, - 3,118,59,0,939,123,1,0,0,0,940,949,5,103,0,0,941,946,3,118,59,0, - 942,943,5,99,0,0,943,945,3,118,59,0,944,942,1,0,0,0,945,948,1,0, - 0,0,946,944,1,0,0,0,946,947,1,0,0,0,947,950,1,0,0,0,948,946,1,0, - 0,0,949,941,1,0,0,0,949,950,1,0,0,0,950,951,1,0,0,0,951,952,5,104, - 0,0,952,125,1,0,0,0,953,954,7,6,0,0,954,127,1,0,0,0,955,956,5,114, - 0,0,956,129,1,0,0,0,81,142,149,170,182,194,214,222,229,235,247,250, - 256,267,276,286,289,291,295,303,315,320,329,356,380,390,396,425, - 432,436,441,452,458,466,471,482,487,494,503,516,537,540,543,550, - 558,589,601,605,613,625,639,649,667,671,674,678,681,690,694,698, - 704,713,750,769,772,774,785,800,816,825,833,843,852,870,894,900, - 902,921,929,932,946,949 + 108,110,112,114,116,118,120,122,124,126,128,130,0,7,1,0,66,70,2, + 0,61,65,67,68,2,0,61,62,67,68,2,0,63,65,67,68,1,0,86,93,1,0,73,76, + 2,0,40,40,114,114,1036,0,144,1,0,0,0,2,146,1,0,0,0,4,156,1,0,0,0, + 6,160,1,0,0,0,8,184,1,0,0,0,10,196,1,0,0,0,12,198,1,0,0,0,14,205, + 1,0,0,0,16,216,1,0,0,0,18,218,1,0,0,0,20,231,1,0,0,0,22,263,1,0, + 0,0,24,293,1,0,0,0,26,295,1,0,0,0,28,299,1,0,0,0,30,317,1,0,0,0, + 32,319,1,0,0,0,34,331,1,0,0,0,36,334,1,0,0,0,38,351,1,0,0,0,40,385, + 1,0,0,0,42,387,1,0,0,0,44,430,1,0,0,0,46,441,1,0,0,0,48,446,1,0, + 0,0,50,471,1,0,0,0,52,473,1,0,0,0,54,496,1,0,0,0,56,512,1,0,0,0, + 58,525,1,0,0,0,60,528,1,0,0,0,62,548,1,0,0,0,64,550,1,0,0,0,66,558, + 1,0,0,0,68,610,1,0,0,0,70,612,1,0,0,0,72,623,1,0,0,0,74,625,1,0, + 0,0,76,630,1,0,0,0,78,632,1,0,0,0,80,654,1,0,0,0,82,656,1,0,0,0, + 84,665,1,0,0,0,86,690,1,0,0,0,88,719,1,0,0,0,90,721,1,0,0,0,92,728, + 1,0,0,0,94,742,1,0,0,0,96,748,1,0,0,0,98,763,1,0,0,0,100,787,1,0, + 0,0,102,789,1,0,0,0,104,791,1,0,0,0,106,793,1,0,0,0,108,856,1,0, + 0,0,110,858,1,0,0,0,112,915,1,0,0,0,114,917,1,0,0,0,116,922,1,0, + 0,0,118,924,1,0,0,0,120,934,1,0,0,0,122,936,1,0,0,0,124,949,1,0, + 0,0,126,953,1,0,0,0,128,966,1,0,0,0,130,968,1,0,0,0,132,133,3,6, + 3,0,133,134,5,0,0,1,134,145,1,0,0,0,135,136,3,20,10,0,136,137,5, + 0,0,1,137,145,1,0,0,0,138,139,3,48,24,0,139,140,5,0,0,1,140,145, + 1,0,0,0,141,142,3,2,1,0,142,143,5,0,0,1,143,145,1,0,0,0,144,132, + 1,0,0,0,144,135,1,0,0,0,144,138,1,0,0,0,144,141,1,0,0,0,145,1,1, + 0,0,0,146,147,5,7,0,0,147,151,5,102,0,0,148,150,3,8,4,0,149,148, + 1,0,0,0,150,153,1,0,0,0,151,149,1,0,0,0,151,152,1,0,0,0,152,154, + 1,0,0,0,153,151,1,0,0,0,154,155,5,103,0,0,155,3,1,0,0,0,156,157, + 5,8,0,0,157,158,3,130,65,0,158,159,5,99,0,0,159,5,1,0,0,0,160,161, + 5,1,0,0,161,162,3,128,64,0,162,163,5,49,0,0,163,164,3,130,65,0,164, + 165,5,43,0,0,165,166,3,130,65,0,166,167,5,42,0,0,167,168,3,130,65, + 0,168,172,5,102,0,0,169,171,3,8,4,0,170,169,1,0,0,0,171,174,1,0, + 0,0,172,170,1,0,0,0,172,173,1,0,0,0,173,175,1,0,0,0,174,172,1,0, + 0,0,175,176,5,103,0,0,176,7,1,0,0,0,177,185,3,4,2,0,178,185,3,18, + 9,0,179,185,3,10,5,0,180,185,3,74,37,0,181,185,3,86,43,0,182,185, + 3,110,55,0,183,185,3,32,16,0,184,177,1,0,0,0,184,178,1,0,0,0,184, + 179,1,0,0,0,184,180,1,0,0,0,184,181,1,0,0,0,184,182,1,0,0,0,184, + 183,1,0,0,0,185,9,1,0,0,0,186,187,5,8,0,0,187,188,5,11,0,0,188,189, + 3,128,64,0,189,190,5,99,0,0,190,197,1,0,0,0,191,192,5,8,0,0,192, + 193,5,13,0,0,193,194,3,128,64,0,194,195,5,99,0,0,195,197,1,0,0,0, + 196,186,1,0,0,0,196,191,1,0,0,0,197,11,1,0,0,0,198,199,5,9,0,0,199, + 200,5,10,0,0,200,201,3,128,64,0,201,202,5,49,0,0,202,203,3,130,65, + 0,203,204,5,99,0,0,204,13,1,0,0,0,205,206,5,9,0,0,206,207,5,11,0, + 0,207,208,3,128,64,0,208,209,5,43,0,0,209,210,3,130,65,0,210,211, + 5,99,0,0,211,15,1,0,0,0,212,217,3,10,5,0,213,217,3,12,6,0,214,217, + 3,14,7,0,215,217,3,32,16,0,216,212,1,0,0,0,216,213,1,0,0,0,216,214, + 1,0,0,0,216,215,1,0,0,0,217,17,1,0,0,0,218,219,5,10,0,0,219,220, + 3,128,64,0,220,221,5,49,0,0,221,224,3,130,65,0,222,223,5,50,0,0, + 223,225,3,130,65,0,224,222,1,0,0,0,224,225,1,0,0,0,225,226,1,0,0, + 0,226,227,5,99,0,0,227,19,1,0,0,0,228,230,3,16,8,0,229,228,1,0,0, + 0,230,233,1,0,0,0,231,229,1,0,0,0,231,232,1,0,0,0,232,234,1,0,0, + 0,233,231,1,0,0,0,234,235,5,11,0,0,235,237,3,128,64,0,236,238,3, + 22,11,0,237,236,1,0,0,0,237,238,1,0,0,0,238,239,1,0,0,0,239,240, + 5,49,0,0,240,241,3,130,65,0,241,242,5,43,0,0,242,252,3,130,65,0, + 243,244,5,54,0,0,244,249,3,26,13,0,245,246,5,100,0,0,246,248,3,26, + 13,0,247,245,1,0,0,0,248,251,1,0,0,0,249,247,1,0,0,0,249,250,1,0, + 0,0,250,253,1,0,0,0,251,249,1,0,0,0,252,243,1,0,0,0,252,253,1,0, + 0,0,253,254,1,0,0,0,254,258,5,102,0,0,255,257,3,34,17,0,256,255, + 1,0,0,0,257,260,1,0,0,0,258,256,1,0,0,0,258,259,1,0,0,0,259,261, + 1,0,0,0,260,258,1,0,0,0,261,262,5,103,0,0,262,21,1,0,0,0,263,264, + 5,108,0,0,264,269,3,24,12,0,265,266,5,100,0,0,266,268,3,24,12,0, + 267,265,1,0,0,0,268,271,1,0,0,0,269,267,1,0,0,0,269,270,1,0,0,0, + 270,272,1,0,0,0,271,269,1,0,0,0,272,273,5,109,0,0,273,23,1,0,0,0, + 274,275,5,14,0,0,275,278,3,128,64,0,276,277,5,98,0,0,277,279,5,4, + 0,0,278,276,1,0,0,0,278,279,1,0,0,0,279,294,1,0,0,0,280,281,5,3, + 0,0,281,291,3,128,64,0,282,283,5,5,0,0,283,288,3,26,13,0,284,285, + 5,110,0,0,285,287,3,26,13,0,286,284,1,0,0,0,287,290,1,0,0,0,288, + 286,1,0,0,0,288,289,1,0,0,0,289,292,1,0,0,0,290,288,1,0,0,0,291, + 282,1,0,0,0,291,292,1,0,0,0,292,294,1,0,0,0,293,274,1,0,0,0,293, + 280,1,0,0,0,294,25,1,0,0,0,295,297,3,128,64,0,296,298,3,28,14,0, + 297,296,1,0,0,0,297,298,1,0,0,0,298,27,1,0,0,0,299,300,5,108,0,0, + 300,305,3,30,15,0,301,302,5,100,0,0,302,304,3,30,15,0,303,301,1, + 0,0,0,304,307,1,0,0,0,305,303,1,0,0,0,305,306,1,0,0,0,306,308,1, + 0,0,0,307,305,1,0,0,0,308,309,5,109,0,0,309,29,1,0,0,0,310,311,5, + 10,0,0,311,318,3,128,64,0,312,313,5,11,0,0,313,318,3,26,13,0,314, + 315,5,3,0,0,315,318,3,128,64,0,316,318,3,112,56,0,317,310,1,0,0, + 0,317,312,1,0,0,0,317,314,1,0,0,0,317,316,1,0,0,0,318,31,1,0,0,0, + 319,320,5,2,0,0,320,322,3,128,64,0,321,323,3,22,11,0,322,321,1,0, + 0,0,322,323,1,0,0,0,323,324,1,0,0,0,324,325,5,111,0,0,325,326,3, + 112,56,0,326,327,5,99,0,0,327,33,1,0,0,0,328,332,3,38,19,0,329,332, + 3,42,21,0,330,332,3,36,18,0,331,328,1,0,0,0,331,329,1,0,0,0,331, + 330,1,0,0,0,332,35,1,0,0,0,333,335,5,24,0,0,334,333,1,0,0,0,334, + 335,1,0,0,0,335,336,1,0,0,0,336,337,5,16,0,0,337,338,3,128,64,0, + 338,339,5,49,0,0,339,340,3,130,65,0,340,341,5,98,0,0,341,342,3,112, + 56,0,342,343,5,97,0,0,343,344,3,112,56,0,344,345,5,102,0,0,345,346, + 5,66,0,0,346,347,5,49,0,0,347,348,3,130,65,0,348,349,5,99,0,0,349, + 350,5,103,0,0,350,37,1,0,0,0,351,352,5,14,0,0,352,353,3,128,64,0, + 353,354,5,49,0,0,354,355,3,130,65,0,355,356,5,98,0,0,356,357,3,112, + 56,0,357,361,5,102,0,0,358,360,3,40,20,0,359,358,1,0,0,0,360,363, + 1,0,0,0,361,359,1,0,0,0,361,362,1,0,0,0,362,364,1,0,0,0,363,361, + 1,0,0,0,364,365,5,103,0,0,365,39,1,0,0,0,366,367,5,56,0,0,367,368, + 5,49,0,0,368,369,3,130,65,0,369,370,5,99,0,0,370,386,1,0,0,0,371, + 372,5,57,0,0,372,373,5,49,0,0,373,374,3,130,65,0,374,375,5,99,0, + 0,375,386,1,0,0,0,376,377,5,58,0,0,377,378,5,59,0,0,378,379,5,49, + 0,0,379,380,3,130,65,0,380,381,5,60,0,0,381,382,5,49,0,0,382,383, + 3,130,65,0,383,384,5,99,0,0,384,386,1,0,0,0,385,366,1,0,0,0,385, + 371,1,0,0,0,385,376,1,0,0,0,386,41,1,0,0,0,387,388,5,15,0,0,388, + 389,3,128,64,0,389,390,5,49,0,0,390,391,3,130,65,0,391,392,5,98, + 0,0,392,393,3,118,59,0,393,395,3,46,23,0,394,396,5,77,0,0,395,394, + 1,0,0,0,395,396,1,0,0,0,396,397,1,0,0,0,397,401,5,102,0,0,398,400, + 3,44,22,0,399,398,1,0,0,0,400,403,1,0,0,0,401,399,1,0,0,0,401,402, + 1,0,0,0,402,404,1,0,0,0,403,401,1,0,0,0,404,405,5,103,0,0,405,43, + 1,0,0,0,406,407,5,63,0,0,407,408,5,49,0,0,408,409,3,130,65,0,409, + 410,5,99,0,0,410,431,1,0,0,0,411,412,5,64,0,0,412,413,5,49,0,0,413, + 414,3,130,65,0,414,415,5,99,0,0,415,431,1,0,0,0,416,417,5,65,0,0, + 417,418,5,49,0,0,418,419,3,130,65,0,419,420,5,99,0,0,420,431,1,0, + 0,0,421,422,5,58,0,0,422,423,5,59,0,0,423,424,5,49,0,0,424,425,3, + 130,65,0,425,426,5,60,0,0,426,427,5,49,0,0,427,428,3,130,65,0,428, + 429,5,99,0,0,429,431,1,0,0,0,430,406,1,0,0,0,430,411,1,0,0,0,430, + 416,1,0,0,0,430,421,1,0,0,0,431,45,1,0,0,0,432,433,5,10,0,0,433, + 442,3,128,64,0,434,435,5,11,0,0,435,437,3,128,64,0,436,438,3,28, + 14,0,437,436,1,0,0,0,437,438,1,0,0,0,438,442,1,0,0,0,439,440,5,3, + 0,0,440,442,3,128,64,0,441,432,1,0,0,0,441,434,1,0,0,0,441,439,1, + 0,0,0,442,47,1,0,0,0,443,445,3,16,8,0,444,443,1,0,0,0,445,448,1, + 0,0,0,446,444,1,0,0,0,446,447,1,0,0,0,447,449,1,0,0,0,448,446,1, + 0,0,0,449,450,5,13,0,0,450,451,3,128,64,0,451,452,5,49,0,0,452,453, + 3,130,65,0,453,454,5,43,0,0,454,457,3,130,65,0,455,456,5,44,0,0, + 456,458,5,112,0,0,457,455,1,0,0,0,457,458,1,0,0,0,458,459,1,0,0, + 0,459,463,5,102,0,0,460,462,3,50,25,0,461,460,1,0,0,0,462,465,1, + 0,0,0,463,461,1,0,0,0,463,464,1,0,0,0,464,466,1,0,0,0,465,463,1, + 0,0,0,466,467,5,103,0,0,467,49,1,0,0,0,468,472,3,52,26,0,469,472, + 3,54,27,0,470,472,3,56,28,0,471,468,1,0,0,0,471,469,1,0,0,0,471, + 470,1,0,0,0,472,51,1,0,0,0,473,474,5,16,0,0,474,476,3,128,64,0,475, + 477,3,22,11,0,476,475,1,0,0,0,476,477,1,0,0,0,477,478,1,0,0,0,478, + 479,5,49,0,0,479,480,3,130,65,0,480,481,5,98,0,0,481,482,3,112,56, + 0,482,483,5,97,0,0,483,484,3,112,56,0,484,485,5,51,0,0,485,487,3, + 60,30,0,486,488,3,58,29,0,487,486,1,0,0,0,487,488,1,0,0,0,488,489, + 1,0,0,0,489,490,5,53,0,0,490,492,3,62,31,0,491,493,3,66,33,0,492, + 491,1,0,0,0,492,493,1,0,0,0,493,494,1,0,0,0,494,495,5,99,0,0,495, + 53,1,0,0,0,496,497,5,17,0,0,497,499,3,128,64,0,498,500,3,22,11,0, + 499,498,1,0,0,0,499,500,1,0,0,0,500,501,1,0,0,0,501,502,5,49,0,0, + 502,503,3,130,65,0,503,504,5,98,0,0,504,505,3,112,56,0,505,506,5, + 97,0,0,506,508,3,112,56,0,507,509,3,66,33,0,508,507,1,0,0,0,508, + 509,1,0,0,0,509,510,1,0,0,0,510,511,5,99,0,0,511,55,1,0,0,0,512, + 513,5,18,0,0,513,514,3,128,64,0,514,515,5,49,0,0,515,516,3,130,65, + 0,516,517,5,19,0,0,517,518,3,128,64,0,518,519,5,98,0,0,519,521,3, + 112,56,0,520,522,3,66,33,0,521,520,1,0,0,0,521,522,1,0,0,0,522,523, + 1,0,0,0,523,524,5,99,0,0,524,57,1,0,0,0,525,526,5,52,0,0,526,527, + 3,112,56,0,527,59,1,0,0,0,528,529,7,0,0,0,529,61,1,0,0,0,530,549, + 5,55,0,0,531,532,5,10,0,0,532,549,3,128,64,0,533,534,5,3,0,0,534, + 549,3,128,64,0,535,536,5,12,0,0,536,545,5,104,0,0,537,542,3,26,13, + 0,538,539,5,100,0,0,539,541,3,26,13,0,540,538,1,0,0,0,541,544,1, + 0,0,0,542,540,1,0,0,0,542,543,1,0,0,0,543,546,1,0,0,0,544,542,1, + 0,0,0,545,537,1,0,0,0,545,546,1,0,0,0,546,547,1,0,0,0,547,549,5, + 105,0,0,548,530,1,0,0,0,548,531,1,0,0,0,548,533,1,0,0,0,548,535, + 1,0,0,0,549,63,1,0,0,0,550,555,3,128,64,0,551,552,5,100,0,0,552, + 554,3,128,64,0,553,551,1,0,0,0,554,557,1,0,0,0,555,553,1,0,0,0,555, + 556,1,0,0,0,556,65,1,0,0,0,557,555,1,0,0,0,558,559,5,54,0,0,559, + 563,5,102,0,0,560,562,3,68,34,0,561,560,1,0,0,0,562,565,1,0,0,0, + 563,561,1,0,0,0,563,564,1,0,0,0,564,566,1,0,0,0,565,563,1,0,0,0, + 566,567,5,103,0,0,567,67,1,0,0,0,568,569,5,28,0,0,569,570,3,128, + 64,0,570,571,5,49,0,0,571,572,3,130,65,0,572,573,5,98,0,0,573,574, + 3,112,56,0,574,575,3,70,35,0,575,576,5,99,0,0,576,611,1,0,0,0,577, + 578,5,29,0,0,578,579,3,128,64,0,579,580,5,49,0,0,580,581,3,130,65, + 0,581,582,5,98,0,0,582,583,3,118,59,0,583,584,3,46,23,0,584,585, + 3,70,35,0,585,586,5,99,0,0,586,611,1,0,0,0,587,588,5,11,0,0,588, + 589,3,128,64,0,589,590,5,49,0,0,590,591,3,130,65,0,591,592,5,98, + 0,0,592,594,3,128,64,0,593,595,3,28,14,0,594,593,1,0,0,0,594,595, + 1,0,0,0,595,596,1,0,0,0,596,597,5,99,0,0,597,611,1,0,0,0,598,599, + 5,18,0,0,599,600,3,128,64,0,600,601,5,49,0,0,601,602,3,130,65,0, + 602,603,5,98,0,0,603,606,3,128,64,0,604,605,5,20,0,0,605,607,3,112, + 56,0,606,604,1,0,0,0,606,607,1,0,0,0,607,608,1,0,0,0,608,609,5,99, + 0,0,609,611,1,0,0,0,610,568,1,0,0,0,610,577,1,0,0,0,610,587,1,0, + 0,0,610,598,1,0,0,0,611,69,1,0,0,0,612,613,5,104,0,0,613,618,3,72, + 36,0,614,615,5,100,0,0,615,617,3,72,36,0,616,614,1,0,0,0,617,620, + 1,0,0,0,618,616,1,0,0,0,618,619,1,0,0,0,619,621,1,0,0,0,620,618, + 1,0,0,0,621,622,5,105,0,0,622,71,1,0,0,0,623,624,7,1,0,0,624,73, + 1,0,0,0,625,626,5,27,0,0,626,627,3,76,38,0,627,75,1,0,0,0,628,631, + 3,78,39,0,629,631,3,82,41,0,630,628,1,0,0,0,630,629,1,0,0,0,631, + 77,1,0,0,0,632,633,5,28,0,0,633,634,3,128,64,0,634,635,5,49,0,0, + 635,636,3,130,65,0,636,637,5,37,0,0,637,638,3,128,64,0,638,639,5, + 98,0,0,639,640,3,112,56,0,640,641,5,38,0,0,641,644,3,80,40,0,642, + 643,5,39,0,0,643,645,3,120,60,0,644,642,1,0,0,0,644,645,1,0,0,0, + 645,646,1,0,0,0,646,647,5,99,0,0,647,79,1,0,0,0,648,655,5,71,0,0, + 649,650,5,72,0,0,650,651,5,106,0,0,651,652,3,112,56,0,652,653,5, + 107,0,0,653,655,1,0,0,0,654,648,1,0,0,0,654,649,1,0,0,0,655,81,1, + 0,0,0,656,657,5,29,0,0,657,658,3,128,64,0,658,659,5,49,0,0,659,660, + 3,130,65,0,660,661,5,102,0,0,661,662,3,84,42,0,662,663,3,84,42,0, + 663,664,5,103,0,0,664,83,1,0,0,0,665,666,3,46,23,0,666,667,5,30, + 0,0,667,668,3,128,64,0,668,669,5,49,0,0,669,670,3,130,65,0,670,672, + 3,118,59,0,671,673,5,77,0,0,672,671,1,0,0,0,672,673,1,0,0,0,673, + 676,1,0,0,0,674,675,5,45,0,0,675,677,3,130,65,0,676,674,1,0,0,0, + 676,677,1,0,0,0,677,679,1,0,0,0,678,680,5,46,0,0,679,678,1,0,0,0, + 679,680,1,0,0,0,680,683,1,0,0,0,681,682,5,47,0,0,682,684,3,130,65, + 0,683,681,1,0,0,0,683,684,1,0,0,0,684,686,1,0,0,0,685,687,5,48,0, + 0,686,685,1,0,0,0,686,687,1,0,0,0,687,688,1,0,0,0,688,689,5,99,0, + 0,689,85,1,0,0,0,690,691,5,21,0,0,691,692,3,128,64,0,692,693,5,22, + 0,0,693,695,3,128,64,0,694,696,3,28,14,0,695,694,1,0,0,0,695,696, + 1,0,0,0,696,699,1,0,0,0,697,698,5,49,0,0,698,700,3,130,65,0,699, + 697,1,0,0,0,699,700,1,0,0,0,700,703,1,0,0,0,701,702,5,44,0,0,702, + 704,5,112,0,0,703,701,1,0,0,0,703,704,1,0,0,0,704,705,1,0,0,0,705, + 709,5,102,0,0,706,708,3,88,44,0,707,706,1,0,0,0,708,711,1,0,0,0, + 709,707,1,0,0,0,709,710,1,0,0,0,710,712,1,0,0,0,711,709,1,0,0,0, + 712,713,5,103,0,0,713,87,1,0,0,0,714,715,5,26,0,0,715,720,3,76,38, + 0,716,720,3,94,47,0,717,720,3,90,45,0,718,720,3,92,46,0,719,714, + 1,0,0,0,719,716,1,0,0,0,719,717,1,0,0,0,719,718,1,0,0,0,720,89,1, + 0,0,0,721,722,5,23,0,0,722,723,3,128,64,0,723,724,5,25,0,0,724,725, + 5,28,0,0,725,726,3,128,64,0,726,727,5,99,0,0,727,91,1,0,0,0,728, + 729,5,34,0,0,729,730,3,128,64,0,730,731,5,35,0,0,731,732,5,36,0, + 0,732,733,5,32,0,0,733,734,5,18,0,0,734,735,3,128,64,0,735,736,5, + 33,0,0,736,737,5,29,0,0,737,738,3,128,64,0,738,739,5,101,0,0,739, + 740,3,128,64,0,740,741,5,99,0,0,741,93,1,0,0,0,742,743,5,23,0,0, + 743,744,3,96,48,0,744,745,5,25,0,0,745,746,3,100,50,0,746,747,5, + 99,0,0,747,95,1,0,0,0,748,749,3,128,64,0,749,750,5,101,0,0,750,751, + 3,98,49,0,751,97,1,0,0,0,752,764,3,128,64,0,753,764,5,66,0,0,754, + 764,5,56,0,0,755,764,5,57,0,0,756,764,5,63,0,0,757,764,5,64,0,0, + 758,764,5,65,0,0,759,764,5,67,0,0,760,764,5,68,0,0,761,764,5,69, + 0,0,762,764,5,70,0,0,763,752,1,0,0,0,763,753,1,0,0,0,763,754,1,0, + 0,0,763,755,1,0,0,0,763,756,1,0,0,0,763,757,1,0,0,0,763,758,1,0, + 0,0,763,759,1,0,0,0,763,760,1,0,0,0,763,761,1,0,0,0,763,762,1,0, + 0,0,764,99,1,0,0,0,765,766,5,28,0,0,766,767,3,128,64,0,767,768,5, + 101,0,0,768,769,3,102,51,0,769,788,1,0,0,0,770,771,5,29,0,0,771, + 772,3,128,64,0,772,773,5,101,0,0,773,774,3,128,64,0,774,775,5,101, + 0,0,775,776,3,104,52,0,776,788,1,0,0,0,777,778,5,13,0,0,778,779, + 3,128,64,0,779,780,5,101,0,0,780,782,3,128,64,0,781,783,3,28,14, + 0,782,781,1,0,0,0,782,783,1,0,0,0,783,785,1,0,0,0,784,786,3,106, + 53,0,785,784,1,0,0,0,785,786,1,0,0,0,786,788,1,0,0,0,787,765,1,0, + 0,0,787,770,1,0,0,0,787,777,1,0,0,0,788,101,1,0,0,0,789,790,7,2, + 0,0,790,103,1,0,0,0,791,792,7,3,0,0,792,105,1,0,0,0,793,794,5,31, + 0,0,794,798,5,102,0,0,795,797,3,108,54,0,796,795,1,0,0,0,797,800, + 1,0,0,0,798,796,1,0,0,0,798,799,1,0,0,0,799,801,1,0,0,0,800,798, + 1,0,0,0,801,802,5,103,0,0,802,107,1,0,0,0,803,804,3,128,64,0,804, + 805,5,25,0,0,805,806,5,28,0,0,806,813,3,128,64,0,807,808,5,33,0, + 0,808,809,5,29,0,0,809,810,3,128,64,0,810,811,5,101,0,0,811,812, + 3,128,64,0,812,814,1,0,0,0,813,807,1,0,0,0,813,814,1,0,0,0,814,815, + 1,0,0,0,815,816,5,99,0,0,816,857,1,0,0,0,817,818,3,128,64,0,818, + 819,5,25,0,0,819,820,5,29,0,0,820,821,3,128,64,0,821,822,5,101,0, + 0,822,829,3,128,64,0,823,824,5,33,0,0,824,825,5,29,0,0,825,826,3, + 128,64,0,826,827,5,101,0,0,827,828,3,128,64,0,828,830,1,0,0,0,829, + 823,1,0,0,0,829,830,1,0,0,0,830,831,1,0,0,0,831,832,5,99,0,0,832, + 857,1,0,0,0,833,834,3,128,64,0,834,835,5,25,0,0,835,836,5,11,0,0, + 836,838,3,128,64,0,837,839,3,28,14,0,838,837,1,0,0,0,838,839,1,0, + 0,0,839,846,1,0,0,0,840,841,5,33,0,0,841,842,5,29,0,0,842,843,3, + 128,64,0,843,844,5,101,0,0,844,845,3,128,64,0,845,847,1,0,0,0,846, + 840,1,0,0,0,846,847,1,0,0,0,847,848,1,0,0,0,848,849,5,99,0,0,849, + 857,1,0,0,0,850,851,3,128,64,0,851,852,5,25,0,0,852,853,5,18,0,0, + 853,854,3,128,64,0,854,855,5,99,0,0,855,857,1,0,0,0,856,803,1,0, + 0,0,856,817,1,0,0,0,856,833,1,0,0,0,856,850,1,0,0,0,857,109,1,0, + 0,0,858,859,5,18,0,0,859,860,3,128,64,0,860,861,5,25,0,0,861,862, + 3,128,64,0,862,863,5,101,0,0,863,865,3,128,64,0,864,866,3,106,53, + 0,865,864,1,0,0,0,865,866,1,0,0,0,866,867,1,0,0,0,867,868,5,99,0, + 0,868,111,1,0,0,0,869,916,3,116,58,0,870,916,5,78,0,0,871,916,5, + 79,0,0,872,873,5,80,0,0,873,916,3,130,65,0,874,875,5,81,0,0,875, + 876,5,108,0,0,876,877,3,128,64,0,877,878,5,109,0,0,878,916,1,0,0, + 0,879,880,5,82,0,0,880,881,5,108,0,0,881,883,3,128,64,0,882,884, + 3,28,14,0,883,882,1,0,0,0,883,884,1,0,0,0,884,885,1,0,0,0,885,886, + 5,109,0,0,886,916,1,0,0,0,887,888,5,6,0,0,888,889,5,108,0,0,889, + 890,3,128,64,0,890,891,5,109,0,0,891,916,1,0,0,0,892,893,5,83,0, + 0,893,894,5,108,0,0,894,895,3,112,56,0,895,896,5,109,0,0,896,916, + 1,0,0,0,897,898,5,84,0,0,898,899,5,108,0,0,899,900,3,112,56,0,900, + 901,5,109,0,0,901,916,1,0,0,0,902,903,5,85,0,0,903,907,5,102,0,0, + 904,906,3,114,57,0,905,904,1,0,0,0,906,909,1,0,0,0,907,905,1,0,0, + 0,907,908,1,0,0,0,908,910,1,0,0,0,909,907,1,0,0,0,910,916,5,103, + 0,0,911,913,3,128,64,0,912,914,3,28,14,0,913,912,1,0,0,0,913,914, + 1,0,0,0,914,916,1,0,0,0,915,869,1,0,0,0,915,870,1,0,0,0,915,871, + 1,0,0,0,915,872,1,0,0,0,915,874,1,0,0,0,915,879,1,0,0,0,915,887, + 1,0,0,0,915,892,1,0,0,0,915,897,1,0,0,0,915,902,1,0,0,0,915,911, + 1,0,0,0,916,113,1,0,0,0,917,918,3,128,64,0,918,919,5,98,0,0,919, + 920,3,112,56,0,920,921,5,99,0,0,921,115,1,0,0,0,922,923,7,4,0,0, + 923,117,1,0,0,0,924,925,7,5,0,0,925,119,1,0,0,0,926,935,3,130,65, + 0,927,935,5,112,0,0,928,935,5,113,0,0,929,935,5,94,0,0,930,935,5, + 95,0,0,931,935,5,96,0,0,932,935,3,122,61,0,933,935,3,126,63,0,934, + 926,1,0,0,0,934,927,1,0,0,0,934,928,1,0,0,0,934,929,1,0,0,0,934, + 930,1,0,0,0,934,931,1,0,0,0,934,932,1,0,0,0,934,933,1,0,0,0,935, + 121,1,0,0,0,936,945,5,102,0,0,937,942,3,124,62,0,938,939,5,100,0, + 0,939,941,3,124,62,0,940,938,1,0,0,0,941,944,1,0,0,0,942,940,1,0, + 0,0,942,943,1,0,0,0,943,946,1,0,0,0,944,942,1,0,0,0,945,937,1,0, + 0,0,945,946,1,0,0,0,946,947,1,0,0,0,947,948,5,103,0,0,948,123,1, + 0,0,0,949,950,3,130,65,0,950,951,5,98,0,0,951,952,3,120,60,0,952, + 125,1,0,0,0,953,962,5,104,0,0,954,959,3,120,60,0,955,956,5,100,0, + 0,956,958,3,120,60,0,957,955,1,0,0,0,958,961,1,0,0,0,959,957,1,0, + 0,0,959,960,1,0,0,0,960,963,1,0,0,0,961,959,1,0,0,0,962,954,1,0, + 0,0,962,963,1,0,0,0,963,964,1,0,0,0,964,965,5,105,0,0,965,127,1, + 0,0,0,966,967,7,6,0,0,967,129,1,0,0,0,968,969,5,115,0,0,969,131, + 1,0,0,0,82,144,151,172,184,196,216,224,231,237,249,252,258,269,278, + 288,291,293,297,305,317,322,331,334,361,385,395,401,430,437,441, + 446,457,463,471,476,487,492,499,508,521,542,545,548,555,563,594, + 606,610,618,630,644,654,672,676,679,683,686,695,699,703,709,719, + 763,782,785,787,798,813,829,838,846,856,865,883,907,913,915,934, + 942,945,959,962 ]; private static __ATN: antlr.ATN; @@ -5008,6 +5066,9 @@ export class OperationMemberContext extends antlr.ParserRuleContext { public RBRACE(): antlr.TerminalNode { return this.getToken(QuixosCapabilityParser.RBRACE, 0)!; } + public STATIC(): antlr.TerminalNode | null { + return this.getToken(QuixosCapabilityParser.STATIC, 0); + } public override get ruleIndex(): number { return QuixosCapabilityParser.RULE_operationMember; } @@ -6176,6 +6237,9 @@ export class ConformanceItemContext extends antlr.ParserRuleContext { public operationBindingDecl(): OperationBindingDeclContext | null { return this.getRuleContext(0, OperationBindingDeclContext); } + public stateFieldBindingDecl(): StateFieldBindingDeclContext | null { + return this.getRuleContext(0, StateFieldBindingDeclContext); + } public relationshipMaterializationDecl(): RelationshipMaterializationDeclContext | null { return this.getRuleContext(0, RelationshipMaterializationDeclContext); } @@ -6192,6 +6256,44 @@ export class ConformanceItemContext extends antlr.ParserRuleContext { } +export class StateFieldBindingDeclContext extends antlr.ParserRuleContext { + public constructor(parent: antlr.ParserRuleContext | null, invokingState: number) { + super(parent, invokingState); + } + public BIND(): antlr.TerminalNode { + return this.getToken(QuixosCapabilityParser.BIND, 0)!; + } + public identifier(): IdentifierContext[]; + public identifier(i: number): IdentifierContext | null; + public identifier(i?: number): IdentifierContext[] | IdentifierContext | null { + if (i === undefined) { + return this.getRuleContexts(IdentifierContext); + } + + return this.getRuleContext(i, IdentifierContext); + } + public TO(): antlr.TerminalNode { + return this.getToken(QuixosCapabilityParser.TO, 0)!; + } + public STATE(): antlr.TerminalNode { + return this.getToken(QuixosCapabilityParser.STATE, 0)!; + } + public SEMI(): antlr.TerminalNode { + return this.getToken(QuixosCapabilityParser.SEMI, 0)!; + } + public override get ruleIndex(): number { + return QuixosCapabilityParser.RULE_stateFieldBindingDecl; + } + public override accept(visitor: QuixosCapabilityVisitor): Result | null { + if (visitor.visitStateFieldBindingDecl) { + return visitor.visitStateFieldBindingDecl(this); + } else { + return visitor.visitChildren(this); + } + } +} + + export class RelationshipMaterializationDeclContext extends antlr.ParserRuleContext { public constructor(parent: antlr.ParserRuleContext | null, invokingState: number) { super(parent, invokingState); diff --git a/src/capability-language/generated/QuixosCapabilityVisitor.ts b/src/capability-language/generated/QuixosCapabilityVisitor.ts index 83fca76..d0a54ca 100644 --- a/src/capability-language/generated/QuixosCapabilityVisitor.ts +++ b/src/capability-language/generated/QuixosCapabilityVisitor.ts @@ -47,6 +47,7 @@ import { EdgeDeclContext } from "./QuixosCapabilityParser.js"; import { EdgeEndpointContext } from "./QuixosCapabilityParser.js"; import { ConformanceDeclContext } from "./QuixosCapabilityParser.js"; import { ConformanceItemContext } from "./QuixosCapabilityParser.js"; +import { StateFieldBindingDeclContext } from "./QuixosCapabilityParser.js"; import { RelationshipMaterializationDeclContext } from "./QuixosCapabilityParser.js"; import { OperationBindingDeclContext } from "./QuixosCapabilityParser.js"; import { MemberOperationRefContext } from "./QuixosCapabilityParser.js"; @@ -347,6 +348,12 @@ export class QuixosCapabilityVisitor extends AbstractParseTreeVisitor Result; + /** + * Visit a parse tree produced by `QuixosCapabilityParser.stateFieldBindingDecl`. + * @param ctx the parse tree + * @return the visitor result + */ + visitStateFieldBindingDecl?: (ctx: StateFieldBindingDeclContext) => Result; /** * Visit a parse tree produced by `QuixosCapabilityParser.relationshipMaterializationDecl`. * @param ctx the parse tree diff --git a/src/capability-language/parser.ts b/src/capability-language/parser.ts index d8f1747..4a83b3a 100644 --- a/src/capability-language/parser.ts +++ b/src/capability-language/parser.ts @@ -552,6 +552,7 @@ const lowerOperationMember = (state: LoweringState, context: OperationMemberCont inputType, outputType, mode: "call", + ...(context.STATIC() ? { scope: "class" as const } : {}), }, ], }; @@ -702,7 +703,15 @@ const lowerInterfaceTemplate = ( inputType, outputType, operations: [ - signature("call", capabilityId.operation(stringValue(operation.stringLiteral(1))), inputType, outputType), + { + ...signature( + "call", + capabilityId.operation(stringValue(operation.stringLiteral(1))), + inputType, + outputType, + ), + ...(operation.STATIC() ? { scope: "class" as const } : {}), + }, ], }; } @@ -1430,6 +1439,42 @@ const lowerConformance = ( const operationBindings = context .conformanceItem() .flatMap((item) => { + const field = item.stateFieldBindingDecl(); + if (field) { + const memberName = identifier(field.identifier(0)); + const member = interfaceSymbol.definition?.members.find((entry) => entry.displayName === memberName); + const attachment = requireSymbol( + state, + state.attachments, + identifier(field.identifier(1)), + field, + "attachment", + ); + if (!member || member.kind !== "value" || attachment?.attachment.kind !== "state") { + loweringIssue( + state, + field, + "invalid-state-field-binding", + "Field shorthand requires a value member and a state slot", + ); + return []; + } + const primitives = { + get: "read", + set: "write", + "watch-start": "watch-start", + "watch-stop": "watch-stop", + } as const; + const slotId = attachment.attachment.id; + return member.operations.map((operation) => ({ + operationId: operation.id, + binding: { + kind: "state" as const, + slotId, + primitive: primitives[operation.displayName as keyof typeof primitives], + }, + })); + } const bindingContext = item.operationBindingDecl(); if (!bindingContext) { return []; diff --git a/src/capability-language/scaffold-recipes.ts b/src/capability-language/scaffold-recipes.ts index 7a3dd20..13f6f07 100644 --- a/src/capability-language/scaffold-recipes.ts +++ b/src/capability-language/scaffold-recipes.ts @@ -142,7 +142,7 @@ export const scaffoldRecipe = async ( if (react) { create( "src/component.tsx", - `// Props are opaque at the platform boundary until capability generics exist.\nexport default function Component(_props: {camino: unknown; render: unknown; dispatch: (action: unknown) => void}) {\n return

${name}

Edit this component, then run qx-workspace check.

;\n}\n`, + `// Add a checked props export, select it in quixos.check.json options.react,\n// then use its generated ReactResults type. Scaffold bundle does this for you.\nexport default function Component(_props: {camino: Record; render: unknown; dispatch: (action: unknown) => void}) {\n return

${name}

;\n}\n`, ); create( "src/impl/sourceGet.ts", @@ -180,14 +180,7 @@ export const scaffoldRecipe = async ( bindingOutput: "src/gen/qx.ts", ...(react ? { - options: { - messages: { - "org.quixos.web-studio.ReactProps": { - module: "@quixos/camino-package-runtime", - export: "opaqueReactPropsBinding", - }, - }, - }, + options: { react: { propsExports: [] } }, } : {}), }), @@ -394,9 +387,20 @@ export const scaffoldRecipe = async ( for (const [file, content] of Object.entries(spec.initialFiles)) { if ( typeof content !== "string" || - ["quixos.lock", "flake.nix", "quixos.toolchain.json", "quixos.check.json", "package.json"].includes(file) + ["quixos.lock", "flake.nix", "quixos.toolchain.json", "package.json"].includes(file) ) throw new Error(`Not an initial authored file: ${file}`); + if (file === "quixos.check.json") { + const check = JSON.parse(content); + if ( + check.backend !== "typescript" || + check.bindingOutput !== "src/gen/qx.ts" || + Object.keys(check).some((key) => !["backend", "bindingOutput", "options"].includes(key)) + ) + throw new Error( + "Initial check configuration may customize binding options, not the scaffold verification backend or output", + ); + } const existing = files.findIndex((entry) => entry.file === prefix + file); if (existing >= 0) files.splice(existing, 1); create(file, content); diff --git a/src/capability-language/tool-cli.ts b/src/capability-language/tool-cli.ts index feae14a..c407d07 100644 --- a/src/capability-language/tool-cli.ts +++ b/src/capability-language/tool-cli.ts @@ -310,6 +310,9 @@ const main = async () => { throw new Error("Interface scaffold requires name, id, revision and Quixos toolchain source"); const request: StructuralRequest = { kind: "interface", + // Like package scaffolding, declaration creation is provisional. Imports + // can be attached next; the normal check verifies the complete graph. + validation: "syntax", source: spec.source, files: [ { diff --git a/src/capability-model/types.ts b/src/capability-model/types.ts index 2f2c7aa..00602f6 100644 --- a/src/capability-model/types.ts +++ b/src/capability-model/types.ts @@ -110,6 +110,8 @@ export interface InterfaceOperation { inputType: Type; outputType: Type; mode: InterfaceOperationMode; + /** Class capabilities have no object receiver and bind free functions. */ + scope?: "class"; /** Required for watch-start/subscribe and absent for other modes. */ eventType?: Type; } diff --git a/src/capability-model/validation.ts b/src/capability-model/validation.ts index 65c1b47..bcd3e73 100644 --- a/src/capability-model/validation.ts +++ b/src/capability-model/validation.ts @@ -23,6 +23,7 @@ import type { OwnedAttachment, PackageExport, PackageOperationExport, + PackageFunctionExport, PackageRevision, PackageRevisionId, PersistentAttachment, @@ -1423,6 +1424,24 @@ const validateConformances = ( } const operation = operationEntry.operation; const binding = entry.binding; + if (operation.scope === "class" && (!conformance.id || operation.mode !== "call")) { + issue( + issues, + "invalid-package-binding", + bindingPath, + "Class capabilities require an explicit conformance ID and call mode", + ); + continue; + } + if (operation.scope === "class" && binding.kind !== "package") { + issue( + issues, + "invalid-package-binding", + bindingPath, + "Class capabilities must bind a free package function, not instance storage", + ); + continue; + } if (binding.kind === "state") { const attachment = findAttachment(indexes, "state", binding.slotId); if (!attachment || attachment.attachment.kind !== "state") { @@ -1562,31 +1581,49 @@ const validateConformances = ( ); continue; } - if (packageExport.kind !== "operation") { + if (operation.scope === "class" ? packageExport.kind !== "function" : packageExport.kind !== "operation") { issue( issues, "invalid-package-binding", `${bindingPath}.binding.exportId`, - `Package export ${binding.exportId} is ${packageExport.kind}, not an operation`, + `Package export ${binding.exportId} must be ${operation.scope === "class" ? "a free function" : "an instance operation"}`, ); continue; } - if (!signaturesMatch(operation, packageExport)) { + if ( + !signaturesMatch(operation, { + ...packageExport, + mode: packageExport.kind === "operation" ? packageExport.mode : "call", + }) + ) { issue( issues, "invalid-package-binding", `${bindingPath}.binding.exportId`, - `Package export provides ${describeSignature(packageExport)}, but operation requires ${describeSignature(operation)}`, + `Package export signature does not match ${describeSignature(operation)}`, + ); + } + if (packageExport.kind === "operation") + validatePackageReceiver(issues, { + entry: packageExport, + atomId: conformance.atomId, + path: `${bindingPath}.binding.exportId`, + indexes, + requirementGraph, + graphSourceKey: key, + }); + if ( + operation.scope === "class" && + (binding.dependencies.some((entry) => entry.binding.kind !== "constructor") || + packageExport.dependencyPorts.some((entry) => entry.requirement.kind !== "constructor")) + ) { + issue( + issues, + "invalid-package-binding", + bindingPath, + "Class functions may inject constructors, not instance-dependent ports", ); } - validatePackageReceiver(issues, { - entry: packageExport, - atomId: conformance.atomId, - path: `${bindingPath}.binding.exportId`, - indexes, - requirementGraph, - graphSourceKey: key, - }); validateBoundDependencies(issues, { dependencies: binding.dependencies, dependencyPorts: packageExport.dependencyPorts, @@ -1919,7 +1956,7 @@ export type ResolvedOperationPlan = kind: "package"; binding: Extract; packageRevision: PackageRevision; - packageExport: PackageOperationExport; + packageExport: PackageOperationExport | PackageFunctionExport; dependencies: Array<{ port: DependencyPort; binding: DependencyBinding; @@ -1962,7 +1999,8 @@ export const resolveOperationPlan = ( } const packageRevision = plan.packages.get(binding.packageRevisionId); const packageExport = packageRevision?.exports.find( - (entry): entry is PackageOperationExport => entry.id === binding.exportId && entry.kind === "operation", + (entry): entry is PackageOperationExport | PackageFunctionExport => + entry.id === binding.exportId && (entry.kind === "operation" || entry.kind === "function"), ); if (!packageRevision || !packageExport) { return undefined; diff --git a/src/gen/quixos/orch_pb.ts b/src/gen/quixos/orch_pb.ts index 94bcf1b..3b5f13a 100644 --- a/src/gen/quixos/orch_pb.ts +++ b/src/gen/quixos/orch_pb.ts @@ -4,7 +4,7 @@ import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2"; import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2"; -import type { CaminoObject, Value } from "../camino/api_pb.js"; +import type { CaminoObject, CrdtValue, Value } from "../camino/api_pb.js"; import { file_camino_api } from "../camino/api_pb.js"; import type { PackageDescriptor } from "./package_pb.js"; import { file_quixos_package } from "./package_pb.js"; @@ -18,7 +18,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file quixos/orch.proto. */ export const file_quixos_orch: GenFile = /*@__PURE__*/ - fileDesc("ChFxdWl4b3Mvb3JjaC5wcm90bxILcXVpeG9zLm9yY2gipQEKFkNvbnN0cnVjdE9iamVjdFJlcXVlc3QSDwoHYXRvbV9pZBgBIAEoCRI9CgVpbnB1dBgCIAMoCzIuLnF1aXhvcy5vcmNoLkNvbnN0cnVjdE9iamVjdFJlcXVlc3QuSW5wdXRFbnRyeRo7CgpJbnB1dEVudHJ5EgsKA2tleRgBIAEoCRIcCgV2YWx1ZRgCIAEoCzINLmNhbWluby5WYWx1ZToCOAEiPwoXQ29uc3RydWN0T2JqZWN0UmVzcG9uc2USJAoGb2JqZWN0GAEgASgLMhQuY2FtaW5vLkNhbWlub09iamVjdCJtCiZSZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0UmVxdWVzdBIRCglvYmplY3RfaWQYASABKAkSHQoVaW50ZXJmYWNlX3JldmlzaW9uX2lkGAIgASgJEhEKCW1lbWJlcl9pZBgDIAEoCSJkCidSZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0UmVzcG9uc2USJAoGb2JqZWN0GAEgASgLMhQuY2FtaW5vLkNhbWlub09iamVjdBITCgtjb25zdHJ1Y3RlZBgCIAEoCCLwAQoXSW52b2tlQ2FwYWJpbGl0eVJlcXVlc3QSKQoKY2FwYWJpbGl0eRgBIAEoCzIVLnF1aXhvcy5DYXBhYmlsaXR5UmVmEhEKCW9iamVjdF9pZBgCIAEoCRI+CgVpbnB1dBgDIAMoCzIvLnF1aXhvcy5vcmNoLkludm9rZUNhcGFiaWxpdHlSZXF1ZXN0LklucHV0RW50cnkSGgoSY2xpZW50X211dGF0aW9uX2lkGAQgASgJGjsKCklucHV0RW50cnkSCwoDa2V5GAEgASgJEhwKBXZhbHVlGAIgASgLMg0uY2FtaW5vLlZhbHVlOgI4ASLRAQoYSW52b2tlQ2FwYWJpbGl0eVJlc3BvbnNlEhUKDWludm9jYXRpb25faWQYASABKAkSKwoKYWN0aXZhdGlvbhgCIAEoCzIXLnF1aXhvcy5vcmNoLkFjdGl2YXRpb24SCgoCb2sYAyABKAgSHQoGcmVzdWx0GAQgASgLMg0uY2FtaW5vLlZhbHVlEg0KBWVycm9yGAUgASgJEjcKDGRlcGVuZGVuY2llcxgGIAMoCzIhLnF1aXhvcy5ydW50aW1lLkRlcml2ZWREZXBlbmRlbmN5ItIBChZXYXRjaENhcGFiaWxpdHlSZXF1ZXN0EikKCmNhcGFiaWxpdHkYASABKAsyFS5xdWl4b3MuQ2FwYWJpbGl0eVJlZhIRCglvYmplY3RfaWQYAiABKAkSPQoFaW5wdXQYAyADKAsyLi5xdWl4b3Mub3JjaC5XYXRjaENhcGFiaWxpdHlSZXF1ZXN0LklucHV0RW50cnkaOwoKSW5wdXRFbnRyeRILCgNrZXkYASABKAkSHAoFdmFsdWUYAiABKAsyDS5jYW1pbm8uVmFsdWU6AjgBIuMBChRXYXRjaENhcGFiaWxpdHlFdmVudBIVCg1pbnZvY2F0aW9uX2lkGAEgASgJEisKCmFjdGl2YXRpb24YAiABKAsyFy5xdWl4b3Mub3JjaC5BY3RpdmF0aW9uEhAKCHdhdGNoX2lkGAMgASgJEhwKBXZhbHVlGAQgASgLMg0uY2FtaW5vLlZhbHVlEjcKDGRlcGVuZGVuY2llcxgFIAMoCzIhLnF1aXhvcy5ydW50aW1lLkRlcml2ZWREZXBlbmRlbmN5Eg0KBWVycm9yGAYgASgJEg8KB2luaXRpYWwYByABKAgiFQoTR2V0V29ya3NwYWNlUmVxdWVzdCKXAgoUR2V0V29ya3NwYWNlUmVzcG9uc2USFAoMd29ya3NwYWNlX2lkGAEgASgJEh0KFXdvcmtzcGFjZV9yZXZpc2lvbl9pZBgCIAEoCRIaChJzb3VyY2Vfcm9vdF9jb21taXQYAyABKAkSKgoiZW1wdHlfaW5wdXRfY29uc3RydWN0aWJsZV9hdG9tX2lkcxgEIAMoCRI/ChFjYXBhYmlsaXR5X2lucHV0cxgFIAMoCzIkLnF1aXhvcy5vcmNoLkNhcGFiaWxpdHlJbnB1dENvbnRyYWN0EkEKEmNvbnN0cnVjdG9yX2lucHV0cxgGIAMoCzIlLnF1aXhvcy5vcmNoLkNvbnN0cnVjdG9ySW5wdXRDb250cmFjdCJhChdDYXBhYmlsaXR5SW5wdXRDb250cmFjdBIdChVpbnRlcmZhY2VfcmV2aXNpb25faWQYASABKAkSFAoMb3BlcmF0aW9uX2lkGAIgASgJEhEKCXR5cGVfanNvbhgDIAEoCSI+ChhDb25zdHJ1Y3RvcklucHV0Q29udHJhY3QSDwoHYXRvbV9pZBgBIAEoCRIRCgl0eXBlX2pzb24YAiABKAkiGAoWTGlzdEFjdGl2YXRpb25zUmVxdWVzdCIfCh1MaXN0UGFja2FnZURlc2NyaXB0b3JzUmVxdWVzdCJQCh5MaXN0UGFja2FnZURlc2NyaXB0b3JzUmVzcG9uc2USLgoLZGVzY3JpcHRvcnMYASADKAsyGS5xdWl4b3MuUGFja2FnZURlc2NyaXB0b3IiHAoaTGlzdFBhY2thZ2VSdW50aW1lc1JlcXVlc3QiUgobTGlzdFBhY2thZ2VSdW50aW1lc1Jlc3BvbnNlEjMKCHJ1bnRpbWVzGAEgAygLMiEucXVpeG9zLm9yY2guUGFja2FnZVJ1bnRpbWVTdGF0dXMiRwoXTGlzdEFjdGl2YXRpb25zUmVzcG9uc2USLAoLYWN0aXZhdGlvbnMYASADKAsyFy5xdWl4b3Mub3JjaC5BY3RpdmF0aW9uIj8KFkNsb3NlQWN0aXZhdGlvblJlcXVlc3QSFQoNYWN0aXZhdGlvbl9pZBgBIAEoCRIOCgZyZWFzb24YAiABKAkiRgoXQ2xvc2VBY3RpdmF0aW9uUmVzcG9uc2USKwoKYWN0aXZhdGlvbhgBIAEoCzIXLnF1aXhvcy5vcmNoLkFjdGl2YXRpb24i6wEKCkFjdGl2YXRpb24SFQoNYWN0aXZhdGlvbl9pZBgBIAEoCRIoCgZleHBvcnQYAiABKAsyGC5xdWl4b3MuUGFja2FnZUV4cG9ydFJlZhIRCglvYmplY3RfaWQYAyABKAkSDQoFc3RhdGUYBCABKAkSDgoGZGVtYW5kGAUgASgNEhEKCW9wZW5lZF9hdBgGIAEoCRIUCgxsYXN0X3VzZWRfYXQYByABKAkSGAoQaWRsZV9kZWFkbGluZV9hdBgIIAEoCRIRCgljbG9zZWRfYXQYCSABKAkSFAoMY2xvc2VfcmVhc29uGAogASgJIrMCChRQYWNrYWdlUnVudGltZVN0YXR1cxITCgtydW50aW1lX2tleRgBIAEoCRIbChNwYWNrYWdlX3JldmlzaW9uX2lkGAIgASgJEhkKEXNvdXJjZV9yZXBvc2l0b3J5GAMgASgJEhUKDXNvdXJjZV9jb21taXQYBCABKAkSFAoMYnVpbGRfdGFyZ2V0GAUgASgJEhMKC3NlcnZlcl9wYXRoGAYgASgJEgsKA3BpZBgHIAEoDRINCgVzdGF0ZRgIIAEoCRISCgpzdGFydGVkX2F0GAkgASgJEhkKEWxhc3RfaGFuZHNoYWtlX2F0GAogASgJEiAKGHJ1bnRpbWVfcHJvdG9jb2xfdmVyc2lvbhgLIAEoCRIfChdhZHZlcnRpc2VkX2V4cG9ydF9jb3VudBgMIAEoDTKuBwoTT3JjaGVzdHJhdG9yUnVudGltZRJfChBJbnZva2VDYXBhYmlsaXR5EiQucXVpeG9zLm9yY2guSW52b2tlQ2FwYWJpbGl0eVJlcXVlc3QaJS5xdWl4b3Mub3JjaC5JbnZva2VDYXBhYmlsaXR5UmVzcG9uc2USWwoPV2F0Y2hDYXBhYmlsaXR5EiMucXVpeG9zLm9yY2guV2F0Y2hDYXBhYmlsaXR5UmVxdWVzdBohLnF1aXhvcy5vcmNoLldhdGNoQ2FwYWJpbGl0eUV2ZW50MAESXAoPQ29uc3RydWN0T2JqZWN0EiMucXVpeG9zLm9yY2guQ29uc3RydWN0T2JqZWN0UmVxdWVzdBokLnF1aXhvcy5vcmNoLkNvbnN0cnVjdE9iamVjdFJlc3BvbnNlEowBCh9SZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0EjMucXVpeG9zLm9yY2guUmVzb2x2ZU9yQ29uc3RydWN0UmVsYXRlZE9iamVjdFJlcXVlc3QaNC5xdWl4b3Mub3JjaC5SZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0UmVzcG9uc2USUwoMR2V0V29ya3NwYWNlEiAucXVpeG9zLm9yY2guR2V0V29ya3NwYWNlUmVxdWVzdBohLnF1aXhvcy5vcmNoLkdldFdvcmtzcGFjZVJlc3BvbnNlEnEKFkxpc3RQYWNrYWdlRGVzY3JpcHRvcnMSKi5xdWl4b3Mub3JjaC5MaXN0UGFja2FnZURlc2NyaXB0b3JzUmVxdWVzdBorLnF1aXhvcy5vcmNoLkxpc3RQYWNrYWdlRGVzY3JpcHRvcnNSZXNwb25zZRJoChNMaXN0UGFja2FnZVJ1bnRpbWVzEicucXVpeG9zLm9yY2guTGlzdFBhY2thZ2VSdW50aW1lc1JlcXVlc3QaKC5xdWl4b3Mub3JjaC5MaXN0UGFja2FnZVJ1bnRpbWVzUmVzcG9uc2USXAoPTGlzdEFjdGl2YXRpb25zEiMucXVpeG9zLm9yY2guTGlzdEFjdGl2YXRpb25zUmVxdWVzdBokLnF1aXhvcy5vcmNoLkxpc3RBY3RpdmF0aW9uc1Jlc3BvbnNlElwKD0Nsb3NlQWN0aXZhdGlvbhIjLnF1aXhvcy5vcmNoLkNsb3NlQWN0aXZhdGlvblJlcXVlc3QaJC5xdWl4b3Mub3JjaC5DbG9zZUFjdGl2YXRpb25SZXNwb25zZWIGcHJvdG8z", [file_camino_api, file_quixos_package, file_quixos_refs, file_quixos_runtime]); + fileDesc("ChFxdWl4b3Mvb3JjaC5wcm90bxILcXVpeG9zLm9yY2gipQEKFkNvbnN0cnVjdE9iamVjdFJlcXVlc3QSDwoHYXRvbV9pZBgBIAEoCRI9CgVpbnB1dBgCIAMoCzIuLnF1aXhvcy5vcmNoLkNvbnN0cnVjdE9iamVjdFJlcXVlc3QuSW5wdXRFbnRyeRo7CgpJbnB1dEVudHJ5EgsKA2tleRgBIAEoCRIcCgV2YWx1ZRgCIAEoCzINLmNhbWluby5WYWx1ZToCOAEiPwoXQ29uc3RydWN0T2JqZWN0UmVzcG9uc2USJAoGb2JqZWN0GAEgASgLMhQuY2FtaW5vLkNhbWlub09iamVjdCJtCiZSZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0UmVxdWVzdBIRCglvYmplY3RfaWQYASABKAkSHQoVaW50ZXJmYWNlX3JldmlzaW9uX2lkGAIgASgJEhEKCW1lbWJlcl9pZBgDIAEoCSJkCidSZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0UmVzcG9uc2USJAoGb2JqZWN0GAEgASgLMhQuY2FtaW5vLkNhbWlub09iamVjdBITCgtjb25zdHJ1Y3RlZBgCIAEoCCLwAQoXSW52b2tlQ2FwYWJpbGl0eVJlcXVlc3QSKQoKY2FwYWJpbGl0eRgBIAEoCzIVLnF1aXhvcy5DYXBhYmlsaXR5UmVmEhEKCW9iamVjdF9pZBgCIAEoCRI+CgVpbnB1dBgDIAMoCzIvLnF1aXhvcy5vcmNoLkludm9rZUNhcGFiaWxpdHlSZXF1ZXN0LklucHV0RW50cnkSGgoSY2xpZW50X211dGF0aW9uX2lkGAQgASgJGjsKCklucHV0RW50cnkSCwoDa2V5GAEgASgJEhwKBXZhbHVlGAIgASgLMg0uY2FtaW5vLlZhbHVlOgI4ASLOAQocSW52b2tlQ2xhc3NDYXBhYmlsaXR5UmVxdWVzdBIWCg5jb25mb3JtYW5jZV9pZBgBIAEoCRIUCgxvcGVyYXRpb25faWQYAiABKAkSQwoFaW5wdXQYAyADKAsyNC5xdWl4b3Mub3JjaC5JbnZva2VDbGFzc0NhcGFiaWxpdHlSZXF1ZXN0LklucHV0RW50cnkaOwoKSW5wdXRFbnRyeRILCgNrZXkYASABKAkSHAoFdmFsdWUYAiABKAsyDS5jYW1pbm8uVmFsdWU6AjgBIoMCChhJbnZva2VDYXBhYmlsaXR5UmVzcG9uc2USFQoNaW52b2NhdGlvbl9pZBgBIAEoCRIrCgphY3RpdmF0aW9uGAIgASgLMhcucXVpeG9zLm9yY2guQWN0aXZhdGlvbhIKCgJvaxgDIAEoCBIdCgZyZXN1bHQYBCABKAsyDS5jYW1pbm8uVmFsdWUSDQoFZXJyb3IYBSABKAkSNwoMZGVwZW5kZW5jaWVzGAYgAygLMiEucXVpeG9zLnJ1bnRpbWUuRGVyaXZlZERlcGVuZGVuY3kSMAoNZmllbGRfZWRpdGluZxgHIAEoCzIZLnF1aXhvcy5vcmNoLkZpZWxkRWRpdGluZyJ3CgxGaWVsZEVkaXRpbmcSGwoTZ2V0dGVyX29wZXJhdGlvbl9pZBgBIAEoCRIbChNzZXR0ZXJfb3BlcmF0aW9uX2lkGAIgASgJEhUKDWRvY3VtZW50X3R5cGUYAyABKAkSFgoOYmluZGluZ19kaWdlc3QYBCABKAkizgEKGkVkaXRDYXBhYmlsaXR5RmllbGRSZXF1ZXN0EikKCmNhcGFiaWxpdHkYASABKAsyFS5xdWl4b3MuQ2FwYWJpbGl0eVJlZhIRCglvYmplY3RfaWQYAiABKAkSGwoTc2V0dGVyX29wZXJhdGlvbl9pZBgDIAEoCRIWCg5iaW5kaW5nX2RpZ2VzdBgEIAEoCRIhCgZ1cGRhdGUYBSABKAsyES5jYW1pbm8uQ3JkdFZhbHVlEhoKEmNsaWVudF9tdXRhdGlvbl9pZBgGIAEoCSLSAQoWV2F0Y2hDYXBhYmlsaXR5UmVxdWVzdBIpCgpjYXBhYmlsaXR5GAEgASgLMhUucXVpeG9zLkNhcGFiaWxpdHlSZWYSEQoJb2JqZWN0X2lkGAIgASgJEj0KBWlucHV0GAMgAygLMi4ucXVpeG9zLm9yY2guV2F0Y2hDYXBhYmlsaXR5UmVxdWVzdC5JbnB1dEVudHJ5GjsKCklucHV0RW50cnkSCwoDa2V5GAEgASgJEhwKBXZhbHVlGAIgASgLMg0uY2FtaW5vLlZhbHVlOgI4ASKVAgoUV2F0Y2hDYXBhYmlsaXR5RXZlbnQSFQoNaW52b2NhdGlvbl9pZBgBIAEoCRIrCgphY3RpdmF0aW9uGAIgASgLMhcucXVpeG9zLm9yY2guQWN0aXZhdGlvbhIQCgh3YXRjaF9pZBgDIAEoCRIcCgV2YWx1ZRgEIAEoCzINLmNhbWluby5WYWx1ZRI3CgxkZXBlbmRlbmNpZXMYBSADKAsyIS5xdWl4b3MucnVudGltZS5EZXJpdmVkRGVwZW5kZW5jeRINCgVlcnJvchgGIAEoCRIPCgdpbml0aWFsGAcgASgIEjAKDWZpZWxkX2VkaXRpbmcYCCABKAsyGS5xdWl4b3Mub3JjaC5GaWVsZEVkaXRpbmciOgoTR2V0V29ya3NwYWNlUmVxdWVzdBIjChtpbmNsdWRlX2ludGVyZmFjZV9jb250cmFjdHMYASABKAgi6gIKFEdldFdvcmtzcGFjZVJlc3BvbnNlEhQKDHdvcmtzcGFjZV9pZBgBIAEoCRIdChV3b3Jrc3BhY2VfcmV2aXNpb25faWQYAiABKAkSGgoSc291cmNlX3Jvb3RfY29tbWl0GAMgASgJEioKImVtcHR5X2lucHV0X2NvbnN0cnVjdGlibGVfYXRvbV9pZHMYBCADKAkSPwoRY2FwYWJpbGl0eV9pbnB1dHMYBSADKAsyJC5xdWl4b3Mub3JjaC5DYXBhYmlsaXR5SW5wdXRDb250cmFjdBJBChJjb25zdHJ1Y3Rvcl9pbnB1dHMYBiADKAsyJS5xdWl4b3Mub3JjaC5Db25zdHJ1Y3RvcklucHV0Q29udHJhY3QSFwoPaW50ZXJmYWNlc19qc29uGAcgASgJEjgKEmNsYXNzX2NhcGFiaWxpdGllcxgIIAMoCzIcLnF1aXhvcy5vcmNoLkNsYXNzQ2FwYWJpbGl0eSK5AQoPQ2xhc3NDYXBhYmlsaXR5EhYKDmNvbmZvcm1hbmNlX2lkGAEgASgJEg8KB2F0b21faWQYAiABKAkSHQoVaW50ZXJmYWNlX3JldmlzaW9uX2lkGAMgASgJEhUKDWRlZmluaXRpb25faWQYBCABKAkSFAoMb3BlcmF0aW9uX2lkGAUgASgJEhcKD2lucHV0X3R5cGVfanNvbhgGIAEoCRIYChBvdXRwdXRfdHlwZV9qc29uGAcgASgJImEKF0NhcGFiaWxpdHlJbnB1dENvbnRyYWN0Eh0KFWludGVyZmFjZV9yZXZpc2lvbl9pZBgBIAEoCRIUCgxvcGVyYXRpb25faWQYAiABKAkSEQoJdHlwZV9qc29uGAMgASgJIj4KGENvbnN0cnVjdG9ySW5wdXRDb250cmFjdBIPCgdhdG9tX2lkGAEgASgJEhEKCXR5cGVfanNvbhgCIAEoCSIYChZMaXN0QWN0aXZhdGlvbnNSZXF1ZXN0Ih8KHUxpc3RQYWNrYWdlRGVzY3JpcHRvcnNSZXF1ZXN0IlAKHkxpc3RQYWNrYWdlRGVzY3JpcHRvcnNSZXNwb25zZRIuCgtkZXNjcmlwdG9ycxgBIAMoCzIZLnF1aXhvcy5QYWNrYWdlRGVzY3JpcHRvciIcChpMaXN0UGFja2FnZVJ1bnRpbWVzUmVxdWVzdCJSChtMaXN0UGFja2FnZVJ1bnRpbWVzUmVzcG9uc2USMwoIcnVudGltZXMYASADKAsyIS5xdWl4b3Mub3JjaC5QYWNrYWdlUnVudGltZVN0YXR1cyJHChdMaXN0QWN0aXZhdGlvbnNSZXNwb25zZRIsCgthY3RpdmF0aW9ucxgBIAMoCzIXLnF1aXhvcy5vcmNoLkFjdGl2YXRpb24iPwoWQ2xvc2VBY3RpdmF0aW9uUmVxdWVzdBIVCg1hY3RpdmF0aW9uX2lkGAEgASgJEg4KBnJlYXNvbhgCIAEoCSJGChdDbG9zZUFjdGl2YXRpb25SZXNwb25zZRIrCgphY3RpdmF0aW9uGAEgASgLMhcucXVpeG9zLm9yY2guQWN0aXZhdGlvbiLrAQoKQWN0aXZhdGlvbhIVCg1hY3RpdmF0aW9uX2lkGAEgASgJEigKBmV4cG9ydBgCIAEoCzIYLnF1aXhvcy5QYWNrYWdlRXhwb3J0UmVmEhEKCW9iamVjdF9pZBgDIAEoCRINCgVzdGF0ZRgEIAEoCRIOCgZkZW1hbmQYBSABKA0SEQoJb3BlbmVkX2F0GAYgASgJEhQKDGxhc3RfdXNlZF9hdBgHIAEoCRIYChBpZGxlX2RlYWRsaW5lX2F0GAggASgJEhEKCWNsb3NlZF9hdBgJIAEoCRIUCgxjbG9zZV9yZWFzb24YCiABKAkiswIKFFBhY2thZ2VSdW50aW1lU3RhdHVzEhMKC3J1bnRpbWVfa2V5GAEgASgJEhsKE3BhY2thZ2VfcmV2aXNpb25faWQYAiABKAkSGQoRc291cmNlX3JlcG9zaXRvcnkYAyABKAkSFQoNc291cmNlX2NvbW1pdBgEIAEoCRIUCgxidWlsZF90YXJnZXQYBSABKAkSEwoLc2VydmVyX3BhdGgYBiABKAkSCwoDcGlkGAcgASgNEg0KBXN0YXRlGAggASgJEhIKCnN0YXJ0ZWRfYXQYCSABKAkSGQoRbGFzdF9oYW5kc2hha2VfYXQYCiABKAkSIAoYcnVudGltZV9wcm90b2NvbF92ZXJzaW9uGAsgASgJEh8KF2FkdmVydGlzZWRfZXhwb3J0X2NvdW50GAwgASgNMoAJChNPcmNoZXN0cmF0b3JSdW50aW1lEl8KEEludm9rZUNhcGFiaWxpdHkSJC5xdWl4b3Mub3JjaC5JbnZva2VDYXBhYmlsaXR5UmVxdWVzdBolLnF1aXhvcy5vcmNoLkludm9rZUNhcGFiaWxpdHlSZXNwb25zZRJlChNFZGl0Q2FwYWJpbGl0eUZpZWxkEicucXVpeG9zLm9yY2guRWRpdENhcGFiaWxpdHlGaWVsZFJlcXVlc3QaJS5xdWl4b3Mub3JjaC5JbnZva2VDYXBhYmlsaXR5UmVzcG9uc2USaQoVSW52b2tlQ2xhc3NDYXBhYmlsaXR5EikucXVpeG9zLm9yY2guSW52b2tlQ2xhc3NDYXBhYmlsaXR5UmVxdWVzdBolLnF1aXhvcy5vcmNoLkludm9rZUNhcGFiaWxpdHlSZXNwb25zZRJbCg9XYXRjaENhcGFiaWxpdHkSIy5xdWl4b3Mub3JjaC5XYXRjaENhcGFiaWxpdHlSZXF1ZXN0GiEucXVpeG9zLm9yY2guV2F0Y2hDYXBhYmlsaXR5RXZlbnQwARJcCg9Db25zdHJ1Y3RPYmplY3QSIy5xdWl4b3Mub3JjaC5Db25zdHJ1Y3RPYmplY3RSZXF1ZXN0GiQucXVpeG9zLm9yY2guQ29uc3RydWN0T2JqZWN0UmVzcG9uc2USjAEKH1Jlc29sdmVPckNvbnN0cnVjdFJlbGF0ZWRPYmplY3QSMy5xdWl4b3Mub3JjaC5SZXNvbHZlT3JDb25zdHJ1Y3RSZWxhdGVkT2JqZWN0UmVxdWVzdBo0LnF1aXhvcy5vcmNoLlJlc29sdmVPckNvbnN0cnVjdFJlbGF0ZWRPYmplY3RSZXNwb25zZRJTCgxHZXRXb3Jrc3BhY2USIC5xdWl4b3Mub3JjaC5HZXRXb3Jrc3BhY2VSZXF1ZXN0GiEucXVpeG9zLm9yY2guR2V0V29ya3NwYWNlUmVzcG9uc2UScQoWTGlzdFBhY2thZ2VEZXNjcmlwdG9ycxIqLnF1aXhvcy5vcmNoLkxpc3RQYWNrYWdlRGVzY3JpcHRvcnNSZXF1ZXN0GisucXVpeG9zLm9yY2guTGlzdFBhY2thZ2VEZXNjcmlwdG9yc1Jlc3BvbnNlEmgKE0xpc3RQYWNrYWdlUnVudGltZXMSJy5xdWl4b3Mub3JjaC5MaXN0UGFja2FnZVJ1bnRpbWVzUmVxdWVzdBooLnF1aXhvcy5vcmNoLkxpc3RQYWNrYWdlUnVudGltZXNSZXNwb25zZRJcCg9MaXN0QWN0aXZhdGlvbnMSIy5xdWl4b3Mub3JjaC5MaXN0QWN0aXZhdGlvbnNSZXF1ZXN0GiQucXVpeG9zLm9yY2guTGlzdEFjdGl2YXRpb25zUmVzcG9uc2USXAoPQ2xvc2VBY3RpdmF0aW9uEiMucXVpeG9zLm9yY2guQ2xvc2VBY3RpdmF0aW9uUmVxdWVzdBokLnF1aXhvcy5vcmNoLkNsb3NlQWN0aXZhdGlvblJlc3BvbnNlYgZwcm90bzM", [file_camino_api, file_quixos_package, file_quixos_refs, file_quixos_runtime]); /** * @generated from message quixos.orch.ConstructObjectRequest @@ -143,6 +143,33 @@ export type InvokeCapabilityRequest = Message<"quixos.orch.InvokeCapabilityReque export const InvokeCapabilityRequestSchema: GenMessage = /*@__PURE__*/ messageDesc(file_quixos_orch, 4); +/** + * @generated from message quixos.orch.InvokeClassCapabilityRequest + */ +export type InvokeClassCapabilityRequest = Message<"quixos.orch.InvokeClassCapabilityRequest"> & { + /** + * @generated from field: string conformance_id = 1; + */ + conformanceId: string; + + /** + * @generated from field: string operation_id = 2; + */ + operationId: string; + + /** + * @generated from field: map input = 3; + */ + input: { [key: string]: Value }; +}; + +/** + * Describes the message quixos.orch.InvokeClassCapabilityRequest. + * Use `create(InvokeClassCapabilityRequestSchema)` to create a new message. + */ +export const InvokeClassCapabilityRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_quixos_orch, 5); + /** * @generated from message quixos.orch.InvokeCapabilityResponse */ @@ -176,6 +203,11 @@ export type InvokeCapabilityResponse = Message<"quixos.orch.InvokeCapabilityResp * @generated from field: repeated quixos.runtime.DerivedDependency dependencies = 6; */ dependencies: DerivedDependency[]; + + /** + * @generated from field: quixos.orch.FieldEditing field_editing = 7; + */ + fieldEditing?: FieldEditing | undefined; }; /** @@ -183,7 +215,85 @@ export type InvokeCapabilityResponse = Message<"quixos.orch.InvokeCapabilityResp * Use `create(InvokeCapabilityResponseSchema)` to create a new message. */ export const InvokeCapabilityResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 5); + messageDesc(file_quixos_orch, 6); + +/** + * Resolved from the checked native getter/setter binding, not Value.source. + * + * @generated from message quixos.orch.FieldEditing + */ +export type FieldEditing = Message<"quixos.orch.FieldEditing"> & { + /** + * @generated from field: string getter_operation_id = 1; + */ + getterOperationId: string; + + /** + * @generated from field: string setter_operation_id = 2; + */ + setterOperationId: string; + + /** + * @generated from field: string document_type = 3; + */ + documentType: string; + + /** + * @generated from field: string binding_digest = 4; + */ + bindingDigest: string; +}; + +/** + * Describes the message quixos.orch.FieldEditing. + * Use `create(FieldEditingSchema)` to create a new message. + */ +export const FieldEditingSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_quixos_orch, 7); + +/** + * @generated from message quixos.orch.EditCapabilityFieldRequest + */ +export type EditCapabilityFieldRequest = Message<"quixos.orch.EditCapabilityFieldRequest"> & { + /** + * The public getter; setter must belong to the same value member. + * + * @generated from field: quixos.CapabilityRef capability = 1; + */ + capability?: CapabilityRef | undefined; + + /** + * @generated from field: string object_id = 2; + */ + objectId: string; + + /** + * @generated from field: string setter_operation_id = 3; + */ + setterOperationId: string; + + /** + * @generated from field: string binding_digest = 4; + */ + bindingDigest: string; + + /** + * @generated from field: camino.CrdtValue update = 5; + */ + update?: CrdtValue | undefined; + + /** + * @generated from field: string client_mutation_id = 6; + */ + clientMutationId: string; +}; + +/** + * Describes the message quixos.orch.EditCapabilityFieldRequest. + * Use `create(EditCapabilityFieldRequestSchema)` to create a new message. + */ +export const EditCapabilityFieldRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_quixos_orch, 8); /** * @generated from message quixos.orch.WatchCapabilityRequest @@ -210,7 +320,7 @@ export type WatchCapabilityRequest = Message<"quixos.orch.WatchCapabilityRequest * Use `create(WatchCapabilityRequestSchema)` to create a new message. */ export const WatchCapabilityRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 6); + messageDesc(file_quixos_orch, 9); /** * @generated from message quixos.orch.WatchCapabilityEvent @@ -250,6 +360,11 @@ export type WatchCapabilityEvent = Message<"quixos.orch.WatchCapabilityEvent"> & * @generated from field: bool initial = 7; */ initial: boolean; + + /** + * @generated from field: quixos.orch.FieldEditing field_editing = 8; + */ + fieldEditing?: FieldEditing | undefined; }; /** @@ -257,12 +372,18 @@ export type WatchCapabilityEvent = Message<"quixos.orch.WatchCapabilityEvent"> & * Use `create(WatchCapabilityEventSchema)` to create a new message. */ export const WatchCapabilityEventSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 7); + messageDesc(file_quixos_orch, 10); /** * @generated from message quixos.orch.GetWorkspaceRequest */ export type GetWorkspaceRequest = Message<"quixos.orch.GetWorkspaceRequest"> & { + /** + * Revision polling must not download the entire interface graph. + * + * @generated from field: bool include_interface_contracts = 1; + */ + includeInterfaceContracts: boolean; }; /** @@ -270,7 +391,7 @@ export type GetWorkspaceRequest = Message<"quixos.orch.GetWorkspaceRequest"> & { * Use `create(GetWorkspaceRequestSchema)` to create a new message. */ export const GetWorkspaceRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 8); + messageDesc(file_quixos_orch, 11); /** * @generated from message quixos.orch.GetWorkspaceResponse @@ -292,8 +413,8 @@ export type GetWorkspaceResponse = Message<"quixos.orch.GetWorkspaceResponse"> & sourceRootCommit: string; /** - * Checked constructors whose wire input can be empty. Web Studio intersects - * this with its temporary Createable marker; the marker is not a factory. + * Checked constructors whose wire input can be empty. The create panel uses + * class factory conformances instead of this constructor inventory. * * @generated from field: repeated string empty_input_constructible_atom_ids = 4; */ @@ -308,6 +429,18 @@ export type GetWorkspaceResponse = Message<"quixos.orch.GetWorkspaceResponse"> & * @generated from field: repeated quixos.orch.ConstructorInputContract constructor_inputs = 6; */ constructorInputs: ConstructorInputContract[]; + + /** + * Exact closed interface contracts used by checked presentation consumers. + * + * @generated from field: string interfaces_json = 7; + */ + interfacesJson: string; + + /** + * @generated from field: repeated quixos.orch.ClassCapability class_capabilities = 8; + */ + classCapabilities: ClassCapability[]; }; /** @@ -315,7 +448,54 @@ export type GetWorkspaceResponse = Message<"quixos.orch.GetWorkspaceResponse"> & * Use `create(GetWorkspaceResponseSchema)` to create a new message. */ export const GetWorkspaceResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 9); + messageDesc(file_quixos_orch, 12); + +/** + * @generated from message quixos.orch.ClassCapability + */ +export type ClassCapability = Message<"quixos.orch.ClassCapability"> & { + /** + * @generated from field: string conformance_id = 1; + */ + conformanceId: string; + + /** + * @generated from field: string atom_id = 2; + */ + atomId: string; + + /** + * @generated from field: string interface_revision_id = 3; + */ + interfaceRevisionId: string; + + /** + * @generated from field: string definition_id = 4; + */ + definitionId: string; + + /** + * @generated from field: string operation_id = 5; + */ + operationId: string; + + /** + * @generated from field: string input_type_json = 6; + */ + inputTypeJson: string; + + /** + * @generated from field: string output_type_json = 7; + */ + outputTypeJson: string; +}; + +/** + * Describes the message quixos.orch.ClassCapability. + * Use `create(ClassCapabilitySchema)` to create a new message. + */ +export const ClassCapabilitySchema: GenMessage = /*@__PURE__*/ + messageDesc(file_quixos_orch, 13); /** * @generated from message quixos.orch.CapabilityInputContract @@ -342,7 +522,7 @@ export type CapabilityInputContract = Message<"quixos.orch.CapabilityInputContra * Use `create(CapabilityInputContractSchema)` to create a new message. */ export const CapabilityInputContractSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 10); + messageDesc(file_quixos_orch, 14); /** * @generated from message quixos.orch.ConstructorInputContract @@ -364,7 +544,7 @@ export type ConstructorInputContract = Message<"quixos.orch.ConstructorInputCont * Use `create(ConstructorInputContractSchema)` to create a new message. */ export const ConstructorInputContractSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 11); + messageDesc(file_quixos_orch, 15); /** * @generated from message quixos.orch.ListActivationsRequest @@ -377,7 +557,7 @@ export type ListActivationsRequest = Message<"quixos.orch.ListActivationsRequest * Use `create(ListActivationsRequestSchema)` to create a new message. */ export const ListActivationsRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 12); + messageDesc(file_quixos_orch, 16); /** * @generated from message quixos.orch.ListPackageDescriptorsRequest @@ -390,7 +570,7 @@ export type ListPackageDescriptorsRequest = Message<"quixos.orch.ListPackageDesc * Use `create(ListPackageDescriptorsRequestSchema)` to create a new message. */ export const ListPackageDescriptorsRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 13); + messageDesc(file_quixos_orch, 17); /** * @generated from message quixos.orch.ListPackageDescriptorsResponse @@ -407,7 +587,7 @@ export type ListPackageDescriptorsResponse = Message<"quixos.orch.ListPackageDes * Use `create(ListPackageDescriptorsResponseSchema)` to create a new message. */ export const ListPackageDescriptorsResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 14); + messageDesc(file_quixos_orch, 18); /** * @generated from message quixos.orch.ListPackageRuntimesRequest @@ -420,7 +600,7 @@ export type ListPackageRuntimesRequest = Message<"quixos.orch.ListPackageRuntime * Use `create(ListPackageRuntimesRequestSchema)` to create a new message. */ export const ListPackageRuntimesRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 15); + messageDesc(file_quixos_orch, 19); /** * @generated from message quixos.orch.ListPackageRuntimesResponse @@ -437,7 +617,7 @@ export type ListPackageRuntimesResponse = Message<"quixos.orch.ListPackageRuntim * Use `create(ListPackageRuntimesResponseSchema)` to create a new message. */ export const ListPackageRuntimesResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 16); + messageDesc(file_quixos_orch, 20); /** * @generated from message quixos.orch.ListActivationsResponse @@ -454,7 +634,7 @@ export type ListActivationsResponse = Message<"quixos.orch.ListActivationsRespon * Use `create(ListActivationsResponseSchema)` to create a new message. */ export const ListActivationsResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 17); + messageDesc(file_quixos_orch, 21); /** * @generated from message quixos.orch.CloseActivationRequest @@ -476,7 +656,7 @@ export type CloseActivationRequest = Message<"quixos.orch.CloseActivationRequest * Use `create(CloseActivationRequestSchema)` to create a new message. */ export const CloseActivationRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 18); + messageDesc(file_quixos_orch, 22); /** * @generated from message quixos.orch.CloseActivationResponse @@ -493,7 +673,7 @@ export type CloseActivationResponse = Message<"quixos.orch.CloseActivationRespon * Use `create(CloseActivationResponseSchema)` to create a new message. */ export const CloseActivationResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 19); + messageDesc(file_quixos_orch, 23); /** * @generated from message quixos.orch.Activation @@ -555,7 +735,7 @@ export type Activation = Message<"quixos.orch.Activation"> & { * Use `create(ActivationSchema)` to create a new message. */ export const ActivationSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 20); + messageDesc(file_quixos_orch, 24); /** * @generated from message quixos.orch.PackageRuntimeStatus @@ -627,7 +807,7 @@ export type PackageRuntimeStatus = Message<"quixos.orch.PackageRuntimeStatus"> & * Use `create(PackageRuntimeStatusSchema)` to create a new message. */ export const PackageRuntimeStatusSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_quixos_orch, 21); + messageDesc(file_quixos_orch, 25); /** * @generated from service quixos.orch.OrchestratorRuntime @@ -641,6 +821,22 @@ export const OrchestratorRuntime: GenService<{ input: typeof InvokeCapabilityRequestSchema; output: typeof InvokeCapabilityResponseSchema; }, + /** + * @generated from rpc quixos.orch.OrchestratorRuntime.EditCapabilityField + */ + editCapabilityField: { + methodKind: "unary"; + input: typeof EditCapabilityFieldRequestSchema; + output: typeof InvokeCapabilityResponseSchema; + }, + /** + * @generated from rpc quixos.orch.OrchestratorRuntime.InvokeClassCapability + */ + invokeClassCapability: { + methodKind: "unary"; + input: typeof InvokeClassCapabilityRequestSchema; + output: typeof InvokeCapabilityResponseSchema; + }, /** * @generated from rpc quixos.orch.OrchestratorRuntime.WatchCapability */ diff --git a/test/react-fields.test.ts b/test/react-fields.test.ts new file mode 100644 index 0000000..40f34ab --- /dev/null +++ b/test/react-fields.test.ts @@ -0,0 +1,187 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { spawnSync } from "node:child_process"; +import { compileCapabilityResourceSource, compileCapabilitySource } from "../src/capability-language/parser.js"; +import { generateReactBindings } from "../src/bindings/react.js"; +import { reactPlatformTypes } from "../src/bindings/react-platform.js"; + +const source = { repository: "https://example.test/fields.git", commit: "a".repeat(40) }; +test("React bindings preserve read-only, writable and nested reference contracts", async (t) => { + const iface = compileCapabilityResourceSource( + `interface Fields id "fields" revision "fields@1" { + value title id "title" : string { get id "title:get"; set id "title:set"; watch start id "watch" stop id "stop"; } + value summary id "summary" : string { get id "summary:get"; } + }`, + { source }, + ); + assert.ok(iface.ok && iface.resource.kind === "interface"); + if (!iface.ok || iface.resource.kind !== "interface") throw new Error("interface failed"); + const pkg = compileCapabilityResourceSource( + `import interface Fields; package P id "p" revision "p@1" { + function props id "props" : unit -> record {fields: interface-ref; caption: string;}; + }`, + { source, environment: { interfaces: new Map([["Fields", iface.resource.revision]]) } }, + ); + assert.ok(pkg.ok && pkg.resource.kind === "package"); + if (!pkg.ok || pkg.resource.kind !== "package") throw new Error("package failed"); + const schema = { + format: "quixos-bindings", + version: 1, + interfaces: [iface.resource.revision], + packages: [pkg.resource.revision], + } as const; + const generated = generateReactBindings( + { ...schema, interfaces: [...schema.interfaces], packages: [...schema.packages] }, + "p@1", + ["props"], + ); + assert.match(generated, /"title": WritableField/); + assert.match(generated, /"summary": ReadableField/); + assert.throws( + () => generateReactBindings({ ...schema, interfaces: [], packages: [...schema.packages] }, "p@1", ["props"]), + /Missing React reference/, + ); + const root = await fs.mkdtemp(path.join(os.tmpdir(), "qx-react-types-")); + t.after(() => fs.rm(root, { recursive: true, force: true })); + await fs.writeFile(path.join(root, "react-props.gen.ts"), generated); + await fs.writeFile( + path.join(root, "platform.d.ts"), + reactPlatformTypes + + '\ndeclare module "react" {export type ReactNode = unknown; export type CSSProperties = {}; export function createElement(...args: unknown[]): unknown;}\n', + ); + await fs.writeFile( + path.join(root, "consumer.ts"), + `import {useLiveField, type ReadableField, type WritableField} from "@quixos/web-studio-react-runtime"; +import type {ReactResults} from "./react-props.gen.js"; +declare const props: ReactResults["props"]; +const [title, setTitle] = useLiveField(props.fields.fields.title); +setTitle("new"); +// @ts-expect-error wrong setter value +setTitle(123); +// @ts-expect-error read-only hook has no setter +const [summary, setSummary] = useLiveField(props.fields.fields.summary); +const [manual, write] = useLiveField(props.fields.fields.summary, {write: async (value: string) => {}}); +write("new"); +const readonly: ReadableField = props.fields.fields.title; +// @ts-expect-error read-only does not satisfy writable +const writable: WritableField = props.fields.fields.summary; +declare const narrow: WritableField<"only">; +// @ts-expect-error writable references are invariant +const widened: WritableField = narrow; +// @ts-expect-error callbacks must accept the field's type +useLiveField(props.fields.fields.summary, {write: async (value: number) => {}}); +`, + ); + const result = spawnSync( + process.execPath, + [ + path.resolve("node_modules/typescript/bin/tsc"), + "--strict", + "--noEmit", + "--skipLibCheck", + "--target", + "ES2022", + path.join(root, "platform.d.ts"), + path.join(root, "consumer.ts"), + ], + { encoding: "utf8", cwd: root }, + ); + assert.equal(result.status, 0, result.stdout + result.stderr); + await fs.writeFile( + path.join(root, "react-props.gen.ts"), + generateReactBindings( + { ...schema, interfaces: [...schema.interfaces], packages: [...schema.packages] }, + "p@1", + ["props"], + [{ module: "./component.js", propsExport: "props" }], + ), + ); + const checkComponent = () => + spawnSync( + process.execPath, + [ + path.resolve("node_modules/typescript/bin/tsc"), + "--strict", + "--noEmit", + "--skipLibCheck", + "--target", + "ES2022", + path.join(root, "platform.d.ts"), + path.join(root, "react-props.gen.ts"), + path.join(root, "component.ts"), + ], + { encoding: "utf8", cwd: root }, + ); + await fs.writeFile( + path.join(root, "component.ts"), + 'import type {ReactResults} from "./react-props.gen.js"; export default function Component(props: {camino: ReactResults["props"]}) {return null;}', + ); + const matching = checkComponent(); + assert.equal(matching.status, 0, matching.stdout + matching.stderr); + await fs.writeFile( + path.join(root, "component.ts"), + 'import type {WritableField} from "@quixos/web-studio-react-runtime"; export default function Component(props: {camino: {fields: {fields: {summary: WritableField}}}}) {return null;}', + ); + const incompatible = checkComponent(); + assert.notEqual(incompatible.status, 0, "component cannot strengthen a read-only prop into a writable field"); + assert.match(incompatible.stdout + incompatible.stderr, /writable|WritableField/); +}); + +test("class factories specialize return types and reject instance implementations or mismatched inputs", () => { + const factory = compileCapabilityResourceSource( + 'interface Factory id "factory" revision "factory@1" {static operation create id "create" : unit -> ref {call id "call";}}', + { source }, + ); + assert.ok(factory.ok && factory.resource.kind === "interface"); + if (!factory.ok || factory.resource.kind !== "interface") throw new Error("factory failed"); + const factoryRevision = factory.resource.revision; + const compile = (declaration: string) => { + const pkg = compileCapabilityResourceSource( + `external atom Note id "note"; package P id "p" revision "p@1" {${declaration}}`, + { source }, + ); + assert.ok(pkg.ok && pkg.resource.kind === "package", JSON.stringify(pkg.diagnostics)); + if (!pkg.ok || pkg.resource.kind !== "package") throw new Error("package failed"); + return compileCapabilitySource( + `workspace W id "w" revision "w@1" commit "${source.commit}" { + atom Note id "note"; import interface Factory; import package P; + conform Note as Factory id "factory-conformance" {bind create.call to package P.make;} + }`, + "workspace.qx", + { interfaces: new Map([["Factory", factoryRevision]]), packages: new Map([["P", pkg.resource.revision]]) }, + ); + }; + const good = compile('function make id "make" : unit -> atom-ref;'); + assert.ok(good.ok, JSON.stringify(good.diagnostics)); + assert.equal(good.workspace.interfaceImports[0].members[0].operations[0].scope, "class"); + const wrongInput = compile('function make id "make" : string -> atom-ref;'); + assert.equal(wrongInput.ok, false); + const wrongReceiver = compile('operation make id "make" : unit -> atom-ref mode call receiver atom Note;'); + assert.equal(wrongReceiver.ok, false); + assert.match(JSON.stringify(wrongReceiver.diagnostics), /free function/); +}); + +test("state-field shorthand binds exactly the declared accessors, never invents writes", () => { + const iface = compileCapabilityResourceSource( + 'interface Reader id "reader" revision "reader@1" {value title id "title" : string {get id "get"; watch start id "watch" stop id "stop";}}', + { source }, + ); + assert.ok(iface.ok && iface.resource.kind === "interface"); + if (!iface.ok || iface.resource.kind !== "interface") throw new Error("interface failed"); + const result = compileCapabilitySource( + `workspace W id "w" revision "w@1" commit "${source.commit}" { + atom Note id "note"; import interface Reader; + conform Note as Reader id "reader-conformance" {private state Title id "title-slot" on Note : string policy crdt(string) default ""; bind title to state Title;} + }`, + "workspace.qx", + { interfaces: new Map([["Reader", iface.resource.revision]]) }, + ); + assert.ok(result.ok, JSON.stringify(result.diagnostics)); + assert.deepEqual( + result.workspace.conformances[0].operationBindings.map((binding) => binding.operationId), + ["get", "watch", "stop"], + ); +}); diff --git a/test/scaffold-recipes.test.ts b/test/scaffold-recipes.test.ts index f933301..ba51e34 100644 --- a/test/scaffold-recipes.test.ts +++ b/test/scaffold-recipes.test.ts @@ -30,11 +30,9 @@ test("React preset applies its browser build script and shared-platform imports" assert.match(await fs.readFile(path.join(root, "flake.nix"), "utf8"), /browserSources = true/); assert.equal(await fs.readFile(path.join(root, "src/gen/web-studio-react-runtime.d.ts"), "utf8"), reactPlatformTypes); assert.match(await fs.readFile(path.join(root, "src/browser-assets.d.ts"), "utf8"), /declare module "\*\.css"/); - assert.equal( - JSON.parse(await fs.readFile(path.join(root, "quixos.check.json"), "utf8")).options.messages[ - "org.quixos.web-studio.ReactProps" - ].export, - "opaqueReactPropsBinding", + assert.deepEqual( + JSON.parse(await fs.readFile(path.join(root, "quixos.check.json"), "utf8")).options.react.propsExports, + [], ); await assert.rejects(fs.access(path.join(root, "quixos.scaffold.json"))); assert.equal(