semantifold 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -15
- package/build/index.d.ts +52 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +60 -8
- package/build/src/artifact-path.d.ts +23 -1
- package/build/src/artifact-path.d.ts.map +1 -1
- package/build/src/artifact-path.js +45 -3
- package/build/src/artifacts.d.ts.map +1 -1
- package/build/src/artifacts.js +66 -1
- package/build/src/backends/android.d.ts +49 -0
- package/build/src/backends/android.d.ts.map +1 -0
- package/build/src/backends/android.js +1274 -0
- package/build/src/backends/c-validation.d.ts.map +1 -1
- package/build/src/backends/c-validation.js +7 -3
- package/build/src/backends/cpp-validation.d.ts.map +1 -1
- package/build/src/backends/cpp-validation.js +7 -3
- package/build/src/backends/csharp.js +7 -1
- package/build/src/backends/dart-runtime.d.ts +2 -0
- package/build/src/backends/dart-runtime.d.ts.map +1 -0
- package/build/src/backends/dart-runtime.js +41 -0
- package/build/src/backends/dart.d.ts +20 -0
- package/build/src/backends/dart.d.ts.map +1 -0
- package/build/src/backends/dart.js +321 -0
- package/build/src/backends/effects.d.ts +60 -0
- package/build/src/backends/effects.d.ts.map +1 -0
- package/build/src/backends/effects.js +746 -0
- package/build/src/backends/go.js +18 -4
- package/build/src/backends/identifiers.d.ts +36 -4
- package/build/src/backends/identifiers.d.ts.map +1 -1
- package/build/src/backends/identifiers.js +160 -10
- package/build/src/backends/index.d.ts.map +1 -1
- package/build/src/backends/index.js +27 -4
- package/build/src/backends/ios.d.ts +68 -0
- package/build/src/backends/ios.d.ts.map +1 -0
- package/build/src/backends/ios.js +1503 -0
- package/build/src/backends/java.d.ts.map +1 -1
- package/build/src/backends/java.js +429 -36
- package/build/src/backends/javascript.d.ts +6 -0
- package/build/src/backends/javascript.d.ts.map +1 -1
- package/build/src/backends/javascript.js +380 -24
- package/build/src/backends/kotlin-runtime.d.ts +3 -0
- package/build/src/backends/kotlin-runtime.d.ts.map +1 -0
- package/build/src/backends/kotlin-runtime.js +29 -0
- package/build/src/backends/kotlin.d.ts +33 -0
- package/build/src/backends/kotlin.d.ts.map +1 -0
- package/build/src/backends/kotlin.js +138 -0
- package/build/src/backends/native-validation.d.ts +2 -2
- package/build/src/backends/native-validation.d.ts.map +1 -1
- package/build/src/backends/native-validation.js +2 -2
- package/build/src/backends/ordered-expressions.d.ts.map +1 -1
- package/build/src/backends/ordered-expressions.js +39 -7
- package/build/src/backends/php-stdlib-support.d.ts +19 -0
- package/build/src/backends/php-stdlib-support.d.ts.map +1 -0
- package/build/src/backends/php-stdlib-support.js +227 -0
- package/build/src/backends/php.d.ts.map +1 -1
- package/build/src/backends/php.js +448 -33
- package/build/src/backends/program.d.ts +83 -0
- package/build/src/backends/program.d.ts.map +1 -0
- package/build/src/backends/program.js +1309 -0
- package/build/src/backends/python.js +7 -1
- package/build/src/backends/ruby.d.ts.map +1 -1
- package/build/src/backends/ruby.js +310 -24
- package/build/src/backends/rust-runtime.d.ts +7 -0
- package/build/src/backends/rust-runtime.d.ts.map +1 -0
- package/build/src/backends/rust-runtime.js +32 -0
- package/build/src/backends/rust-validation.d.ts +9 -0
- package/build/src/backends/rust-validation.d.ts.map +1 -0
- package/build/src/backends/rust-validation.js +78 -0
- package/build/src/backends/rust.d.ts +24 -0
- package/build/src/backends/rust.d.ts.map +1 -0
- package/build/src/backends/rust.js +344 -0
- package/build/src/backends/scalars.d.ts +14 -6
- package/build/src/backends/scalars.d.ts.map +1 -1
- package/build/src/backends/scalars.js +167 -17
- package/build/src/backends/shared.d.ts +23 -4
- package/build/src/backends/shared.d.ts.map +1 -1
- package/build/src/backends/shared.js +2174 -62
- package/build/src/backends/stdlib-support.d.ts +14 -0
- package/build/src/backends/stdlib-support.d.ts.map +1 -0
- package/build/src/backends/stdlib-support.js +237 -0
- package/build/src/backends/swift-runtime.d.ts +3 -0
- package/build/src/backends/swift-runtime.d.ts.map +1 -0
- package/build/src/backends/swift-runtime.js +14 -0
- package/build/src/backends/swift.d.ts +8 -0
- package/build/src/backends/swift.d.ts.map +1 -0
- package/build/src/backends/swift.js +127 -0
- package/build/src/backends/typescript.d.ts.map +1 -1
- package/build/src/backends/typescript.js +344 -24
- package/build/src/backends/wasm.d.ts +43 -0
- package/build/src/backends/wasm.d.ts.map +1 -0
- package/build/src/backends/wasm.js +2164 -0
- package/build/src/backends/writer.d.ts +255 -4
- package/build/src/backends/writer.d.ts.map +1 -1
- package/build/src/backends/writer.js +491 -3
- package/build/src/diagnostic.d.ts +4 -3
- package/build/src/diagnostic.d.ts.map +1 -1
- package/build/src/diagnostic.js +5 -3
- package/build/src/frontends/c.d.ts.map +1 -1
- package/build/src/frontends/c.js +5 -3
- package/build/src/frontends/cpp.d.ts.map +1 -1
- package/build/src/frontends/cpp.js +5 -3
- package/build/src/frontends/csharp.d.ts.map +1 -1
- package/build/src/frontends/csharp.js +7 -3
- package/build/src/frontends/dart.d.ts +10 -0
- package/build/src/frontends/dart.d.ts.map +1 -0
- package/build/src/frontends/dart.js +913 -0
- package/build/src/frontends/index.d.ts +3 -1
- package/build/src/frontends/index.d.ts.map +1 -1
- package/build/src/frontends/index.js +15 -5
- package/build/src/frontends/java.d.ts +110 -1
- package/build/src/frontends/java.d.ts.map +1 -1
- package/build/src/frontends/java.js +1725 -70
- package/build/src/frontends/javascript-typescript.d.ts +69 -1
- package/build/src/frontends/javascript-typescript.d.ts.map +1 -1
- package/build/src/frontends/javascript-typescript.js +1825 -93
- package/build/src/frontends/kotlin.d.ts +10 -0
- package/build/src/frontends/kotlin.d.ts.map +1 -0
- package/build/src/frontends/kotlin.js +685 -0
- package/build/src/frontends/php.d.ts +118 -1
- package/build/src/frontends/php.d.ts.map +1 -1
- package/build/src/frontends/php.js +1583 -76
- package/build/src/frontends/program.d.ts +74 -0
- package/build/src/frontends/program.d.ts.map +1 -0
- package/build/src/frontends/program.js +989 -0
- package/build/src/frontends/ruby.d.ts +68 -1
- package/build/src/frontends/ruby.d.ts.map +1 -1
- package/build/src/frontends/ruby.js +1446 -101
- package/build/src/frontends/rust-parser.d.ts +11 -0
- package/build/src/frontends/rust-parser.d.ts.map +1 -0
- package/build/src/frontends/rust-parser.js +15 -0
- package/build/src/frontends/rust.d.ts +14 -0
- package/build/src/frontends/rust.d.ts.map +1 -0
- package/build/src/frontends/rust.js +715 -0
- package/build/src/frontends/scalars.d.ts +10 -0
- package/build/src/frontends/scalars.d.ts.map +1 -1
- package/build/src/frontends/scalars.js +28 -2
- package/build/src/frontends/swift.d.ts +10 -0
- package/build/src/frontends/swift.d.ts.map +1 -0
- package/build/src/frontends/swift.js +860 -0
- package/build/src/frontends/types.d.ts +186 -0
- package/build/src/frontends/types.d.ts.map +1 -0
- package/build/src/frontends/types.js +634 -0
- package/build/src/language-registry.d.ts +46 -4
- package/build/src/language-registry.d.ts.map +1 -1
- package/build/src/language-registry.js +177 -13
- package/build/src/mapping.d.ts +2 -2
- package/build/src/mapping.d.ts.map +1 -1
- package/build/src/mapping.js +31 -10
- package/build/src/semantic/capabilities.d.ts +66 -0
- package/build/src/semantic/capabilities.d.ts.map +1 -0
- package/build/src/semantic/capabilities.js +649 -0
- package/build/src/semantic/effects.d.ts +26 -0
- package/build/src/semantic/effects.d.ts.map +1 -0
- package/build/src/semantic/effects.js +764 -0
- package/build/src/semantic/generics.d.ts +23 -0
- package/build/src/semantic/generics.d.ts.map +1 -0
- package/build/src/semantic/generics.js +61 -0
- package/build/src/semantic/lifetime.d.ts +37 -0
- package/build/src/semantic/lifetime.d.ts.map +1 -0
- package/build/src/semantic/lifetime.js +527 -0
- package/build/src/semantic/provenance.d.ts +9 -2
- package/build/src/semantic/provenance.d.ts.map +1 -1
- package/build/src/semantic/provenance.js +462 -20
- package/build/src/semantic/scalars.d.ts +11 -0
- package/build/src/semantic/scalars.d.ts.map +1 -1
- package/build/src/semantic/scalars.js +16 -1
- package/build/src/semantic/stdlib.d.ts +254 -0
- package/build/src/semantic/stdlib.d.ts.map +1 -0
- package/build/src/semantic/stdlib.js +880 -0
- package/build/src/semantic/types.d.ts +3108 -178
- package/build/src/semantic/types.d.ts.map +1 -1
- package/build/src/semantic/types.js +730 -15
- package/build/src/semantic/validate.d.ts +85 -5
- package/build/src/semantic/validate.d.ts.map +1 -1
- package/build/src/semantic/validate.js +2576 -98
- package/build/src/stdlib-facades.d.ts +304 -0
- package/build/src/stdlib-facades.d.ts.map +1 -0
- package/build/src/stdlib-facades.js +1167 -0
- package/build/src/stdlib-negotiation.d.ts +69 -0
- package/build/src/stdlib-negotiation.d.ts.map +1 -0
- package/build/src/stdlib-negotiation.js +403 -0
- package/build/src/stdlib-providers.d.ts +72 -0
- package/build/src/stdlib-providers.d.ts.map +1 -0
- package/build/src/stdlib-providers.js +339 -0
- package/build/src/toolchains.d.ts +55 -1
- package/build/src/toolchains.d.ts.map +1 -1
- package/build/src/toolchains.js +15 -4
- package/docs/android.md +79 -0
- package/docs/architecture.md +47 -13
- package/docs/dart.md +60 -0
- package/docs/goals.md +3 -3
- package/docs/ios.md +121 -0
- package/docs/kotlin.md +42 -0
- package/docs/language-support.md +238 -26
- package/docs/multi-file-modules.md +52 -0
- package/docs/parser-qualification.md +121 -8
- package/docs/plans/2026-09-05-standard-library-portability.md +10 -10
- package/docs/rust.md +88 -0
- package/docs/source-maps.md +17 -7
- package/docs/specification.md +5 -0
- package/docs/standard-library-portability.md +39 -8
- package/docs/swift.md +44 -0
- package/docs/testing.md +381 -4
- package/docs/webassembly.md +52 -0
- package/index.js +66 -8
- package/node_modules/semantifold-tree-sitter-legacy-internal/README.md +2 -2
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/LICENSE +21 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/README.md +45 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/binding.gyp +30 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/binding.cc +20 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/binding_test.js +9 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/index.d.ts +28 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/index.js +11 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/grammar.js +1630 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/package.json +64 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/darwin-arm64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/darwin-x64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/linux-arm64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/linux-x64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/win32-arm64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/win32-x64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/highlights.scm +159 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/injections.scm +9 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/tags.scm +60 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/grammar.json +9405 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/node-types.json +5399 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/parser.c +192569 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/scanner.c +393 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/alloc.h +54 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/array.h +290 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/parser.h +266 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/tree-sitter-rust.wasm +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/tree-sitter.json +53 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/package.json +3 -2
- package/node_modules/semantifold-tree-sitter-legacy-internal/src/c.js +5 -4
- package/package.json +5 -2
- package/src/artifact-path.js +47 -2
- package/src/artifacts.js +67 -0
- package/src/backends/android.js +1370 -0
- package/src/backends/c-validation.js +6 -1
- package/src/backends/cpp-validation.js +6 -1
- package/src/backends/csharp.js +3 -0
- package/src/backends/dart-runtime.js +41 -0
- package/src/backends/dart.js +326 -0
- package/src/backends/effects.js +705 -0
- package/src/backends/go.js +14 -2
- package/src/backends/identifiers.js +166 -9
- package/src/backends/index.js +27 -2
- package/src/backends/ios.js +1612 -0
- package/src/backends/java.js +454 -36
- package/src/backends/javascript.js +389 -23
- package/src/backends/kotlin-runtime.js +29 -0
- package/src/backends/kotlin.js +144 -0
- package/src/backends/native-validation.js +1 -1
- package/src/backends/ordered-expressions.js +40 -6
- package/src/backends/php-stdlib-support.js +239 -0
- package/src/backends/php.js +469 -32
- package/src/backends/program.js +1408 -0
- package/src/backends/python.js +3 -0
- package/src/backends/ruby.js +326 -23
- package/src/backends/rust-runtime.js +34 -0
- package/src/backends/rust-validation.js +75 -0
- package/src/backends/rust.js +335 -0
- package/src/backends/scalars.js +141 -16
- package/src/backends/shared.js +2314 -115
- package/src/backends/stdlib-support.js +234 -0
- package/src/backends/swift-runtime.js +14 -0
- package/src/backends/swift.js +132 -0
- package/src/backends/typescript.js +349 -23
- package/src/backends/wasm.js +2208 -0
- package/src/backends/writer.js +545 -2
- package/src/diagnostic.js +4 -2
- package/src/frontends/c.js +4 -2
- package/src/frontends/cpp.js +4 -2
- package/src/frontends/csharp.js +4 -2
- package/src/frontends/dart.js +963 -0
- package/src/frontends/index.js +14 -4
- package/src/frontends/java.js +1977 -69
- package/src/frontends/javascript-typescript.js +2046 -102
- package/src/frontends/kotlin.js +713 -0
- package/src/frontends/php.js +1725 -79
- package/src/frontends/program.js +1062 -0
- package/src/frontends/ruby.js +1624 -98
- package/src/frontends/rust-parser.js +18 -0
- package/src/frontends/rust.js +714 -0
- package/src/frontends/scalars.js +31 -1
- package/src/frontends/swift.js +893 -0
- package/src/frontends/types.js +750 -0
- package/src/language-registry.js +182 -12
- package/src/mapping.js +33 -9
- package/src/semantic/capabilities.js +671 -0
- package/src/semantic/effects.js +737 -0
- package/src/semantic/generics.js +58 -0
- package/src/semantic/lifetime.js +517 -0
- package/src/semantic/provenance.js +412 -19
- package/src/semantic/scalars.js +17 -0
- package/src/semantic/stdlib.js +921 -0
- package/src/semantic/types.js +810 -14
- package/src/semantic/validate.js +2934 -209
- package/src/stdlib-facades.js +1211 -0
- package/src/stdlib-negotiation.js +446 -0
- package/src/stdlib-providers.js +377 -0
- package/src/toolchains.js +16 -3
|
@@ -0,0 +1,1062 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import {isDenseArray} from "../array.js"
|
|
4
|
+
import {isSafeSourcePath} from "../artifact-path.js"
|
|
5
|
+
import {SemantifoldDiagnostic, semanticFailure, unsupportedCapability, unsupportedRole} from "../diagnostic.js"
|
|
6
|
+
import {languageRegistry} from "../language-registry.js"
|
|
7
|
+
import {normalizeCapabilityAuthority} from "../semantic/capabilities.js"
|
|
8
|
+
import {moduleLocation} from "../semantic/location.js"
|
|
9
|
+
import {annotateParsedModule} from "../semantic/provenance.js"
|
|
10
|
+
import {moduleUncheckedErrorEffects, validateParsedModule} from "../semantic/validate.js"
|
|
11
|
+
import {
|
|
12
|
+
facadeCapabilityAuthority, resolveStdlibFacadeClosure, stdlibFacadeProgramDescriptor, stdlibFacadeRegistry
|
|
13
|
+
} from "../stdlib-facades.js"
|
|
14
|
+
import {inspectJavaScriptTypeScriptModule} from "./javascript-typescript.js"
|
|
15
|
+
import {inspectJavaModule} from "./java.js"
|
|
16
|
+
import {inspectPhpModule} from "./php.js"
|
|
17
|
+
import {inspectRubyModule} from "./ruby.js"
|
|
18
|
+
|
|
19
|
+
const programLanguages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
20
|
+
const moduleIdPattern = /^[a-z][a-z0-9_]*(?:[.-][a-z][a-z0-9_]*)*$/u
|
|
21
|
+
|
|
22
|
+
/** @typedef {{facade?: import("../stdlib-facades.js").StdlibFacadeRecord, filename: string, id: string, language: import("../semantic/types.js").SemanticLanguage, ownership: "application" | "facade", source: string}} ProgramSource */
|
|
23
|
+
/** @typedef {{declarationLocation?: import("../semantic/types.js").SourceLocation, facade?: import("../stdlib-facades.js").StdlibFacadeRecord, importedName: string, importedNameLocation?: import("../semantic/types.js").SourceLocation, localName: string, localNameLocation?: import("../semantic/types.js").SourceLocation, location: import("../semantic/types.js").SourceLocation, namespace?: boolean, pathLocation: import("../semantic/types.js").SourceLocation, specifier: string, stdlibCandidate?: true, typeOnly: boolean}} ProgramImportRequest */
|
|
24
|
+
/** @typedef {{importedName: string, localName: string, location: import("../semantic/types.js").SourceLocation, nativeName: string, symbolKind: import("../semantic/types.js").SemanticDeclarationKind, typeOnly: boolean}} ProgramNativeBinding */
|
|
25
|
+
/** @typedef {{imports: ProgramImportRequest[], exports: {declarationLocation?: import("../semantic/types.js").SourceLocation, exportedName: string, exportedNameLocation?: import("../semantic/types.js").SourceLocation, localName: string, localNameLocation?: import("../semantic/types.js").SourceLocation, location: import("../semantic/types.js").SourceLocation, symbolKind?: import("../semantic/types.js").SemanticDeclarationKind, typeOnly: boolean}[], bindings?: ProgramNativeBinding[], nativeName?: string, nativeReferences?: {form: "constructor-call" | "receiver-call" | "unqualified-call", location: import("../semantic/types.js").SourceLocation, nativeModule: string, symbol: string}[]}} ProgramHeader */
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Parses an explicit caller-supplied multi-source program without filesystem discovery.
|
|
29
|
+
* @param {object} input - Program parse request.
|
|
30
|
+
* @param {string} input.entryModule - Sole entry module identity.
|
|
31
|
+
* @param {{filename: string, id: string, language: import("../semantic/types.js").SemanticLanguage, source: string}[]} input.sources - Complete source set.
|
|
32
|
+
* @param {Readonly<import("../semantic/types.js").CapabilityAuthority> | import("../semantic/types.js").CapabilityAuthorityInput} [input.capabilityAuthority] - Explicit compiler authority.
|
|
33
|
+
* @returns {import("../semantic/types.js").SemanticProgram} Resolved semantic program.
|
|
34
|
+
*/
|
|
35
|
+
export function parseProgramSource(input) {
|
|
36
|
+
if (!isPlainObject(input) || !isDenseArray(input.sources) || input.sources.length == 0 ||
|
|
37
|
+
typeof input.entryModule != "string") invalidProgram("Program parsing requires a non-empty ordered source set and entry module.")
|
|
38
|
+
|
|
39
|
+
const applicationSources = input.sources.map((source, index) => validateSource(source, index))
|
|
40
|
+
const requestedAuthority = normalizeCapabilityAuthority(input.capabilityAuthority)
|
|
41
|
+
const sourceLanguages = new Set(applicationSources.map(({language}) => language))
|
|
42
|
+
|
|
43
|
+
if (sourceLanguages.size != 1) {
|
|
44
|
+
semanticFailure(applicationSources[0].language, "MIXED_SOURCE_LANGUAGES", "A semantic program must use one source-language compatibility profile.", undefined)
|
|
45
|
+
}
|
|
46
|
+
if (requestedAuthority && !languageRegistry.record(applicationSources[0].language).features.effectfulCapabilitiesAndResources) {
|
|
47
|
+
unsupportedCapability(applicationSources[0].language, "Task 034 effectful capabilities and owned resources",
|
|
48
|
+
moduleLocation(applicationSources[0].filename, applicationSources[0].source))
|
|
49
|
+
}
|
|
50
|
+
const byId = new Map()
|
|
51
|
+
const byFilename = new Map()
|
|
52
|
+
|
|
53
|
+
for (const source of applicationSources) {
|
|
54
|
+
if (byId.has(source.id)) semanticFailure(source.language, "DUPLICATE_MODULE", `Duplicate module identity '${source.id}'.`, undefined)
|
|
55
|
+
if (byFilename.has(source.filename)) semanticFailure(source.language, "DUPLICATE_MODULE", `Duplicate module filename '${source.filename}'.`, undefined)
|
|
56
|
+
byId.set(source.id, source)
|
|
57
|
+
byFilename.set(source.filename, source)
|
|
58
|
+
}
|
|
59
|
+
if (!byId.has(input.entryModule)) {
|
|
60
|
+
semanticFailure(applicationSources[0].language, "INVALID_ENTRY_MODULE", `Unknown entry module '${input.entryModule}'.`, undefined)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const headers = new Map(applicationSources.map((source) => [source.id, inspectSource(source)]))
|
|
64
|
+
const facadeRoots = resolveFacadeImports(applicationSources, headers)
|
|
65
|
+
const facadeRecords = resolveStdlibFacadeClosure(facadeRoots)
|
|
66
|
+
|
|
67
|
+
if (facadeRecords.length > 0 && requestedAuthority) {
|
|
68
|
+
const located = applicationSources.flatMap((source) => requiredMapValue(headers, source.id).imports)
|
|
69
|
+
.find(({facade}) => facade)?.location
|
|
70
|
+
|
|
71
|
+
facadeFailure(applicationSources[0].language, "STDLIB_FACADE_AUTHORITY_FORGED",
|
|
72
|
+
"Programs resolved through compiler-owned facades cannot supply their own canonical capability authority.", located)
|
|
73
|
+
}
|
|
74
|
+
const facadeAuthority = facadeCapabilityAuthority(facadeRecords)
|
|
75
|
+
const authority = requestedAuthority ?? (facadeAuthority ? normalizeCapabilityAuthority(facadeAuthority.input) : undefined)
|
|
76
|
+
const facadeSources = facadeRecords.map((facade) => /** @type {ProgramSource} */ ({
|
|
77
|
+
facade,
|
|
78
|
+
filename: facade.source.filename,
|
|
79
|
+
id: facade.source.id,
|
|
80
|
+
language: facade.language,
|
|
81
|
+
ownership: "facade",
|
|
82
|
+
source: facade.source.content
|
|
83
|
+
}))
|
|
84
|
+
|
|
85
|
+
validateFacadeCollisions(applicationSources, headers, facadeSources)
|
|
86
|
+
const sources = [...applicationSources, ...facadeSources]
|
|
87
|
+
|
|
88
|
+
for (const source of facadeSources) {
|
|
89
|
+
byId.set(source.id, source)
|
|
90
|
+
byFilename.set(source.filename, source)
|
|
91
|
+
headers.set(source.id, inspectSource(source))
|
|
92
|
+
}
|
|
93
|
+
const registeredSources = sources.map((source, index) => ({
|
|
94
|
+
content: source.source,
|
|
95
|
+
filename: source.filename,
|
|
96
|
+
id: `source:${index}`,
|
|
97
|
+
language: source.language,
|
|
98
|
+
ownership: source.ownership
|
|
99
|
+
}))
|
|
100
|
+
const sourceIds = new Map(sources.map((source, index) => [source.id, `source:${index}`]))
|
|
101
|
+
|
|
102
|
+
validateNativeModuleIdentities(sources, headers)
|
|
103
|
+
/** @type {Map<string, {source: typeof sources[number], requests: (ReturnType<typeof inspectSource>["imports"][number] & {moduleId: string})[]}>} */
|
|
104
|
+
const graph = new Map()
|
|
105
|
+
|
|
106
|
+
for (const source of sources) {
|
|
107
|
+
const requests = requiredMapValue(headers, source.id).imports.map((request) => ({
|
|
108
|
+
...request,
|
|
109
|
+
moduleId: request.facade?.source.id ?? resolveImportModule(source, request.specifier, request.pathLocation, byFilename, headers)
|
|
110
|
+
}))
|
|
111
|
+
|
|
112
|
+
graph.set(source.id, {requests, source})
|
|
113
|
+
}
|
|
114
|
+
const orderedIds = topologicalOrder(sources, graph)
|
|
115
|
+
/** @type {Map<string, import("../semantic/types.js").SemanticProgramModule>} */
|
|
116
|
+
const parsed = new Map()
|
|
117
|
+
/** @type {Map<string, Set<string>>} */
|
|
118
|
+
const callEffectsByDeclarationId = new Map()
|
|
119
|
+
|
|
120
|
+
for (const moduleId of orderedIds) {
|
|
121
|
+
const {requests, source} = requiredMapValue(graph, moduleId)
|
|
122
|
+
const sourceId = /** @type {string} */ (sourceIds.get(source.id))
|
|
123
|
+
const header = requiredMapValue(headers, moduleId)
|
|
124
|
+
const localNames = new Set()
|
|
125
|
+
const visibleFunctions = new Map()
|
|
126
|
+
const visibleCallEffects = new Map()
|
|
127
|
+
const visibleRecordsByName = new Map()
|
|
128
|
+
const visibleValueRecordsByName = new Map()
|
|
129
|
+
const visibleRecordsById = new Map()
|
|
130
|
+
const visibleClassesByName = new Map()
|
|
131
|
+
const visibleClassesById = new Map()
|
|
132
|
+
const visibleErrorsByName = new Map()
|
|
133
|
+
const visibleErrorsById = new Map()
|
|
134
|
+
const consumedBindings = new Set()
|
|
135
|
+
/** @type {import("../semantic/types.js").SemanticImport[]} */
|
|
136
|
+
const imports = []
|
|
137
|
+
|
|
138
|
+
if (!source.facade && authority) {
|
|
139
|
+
const failureNames = new Set(header.imports.filter(({facade}) => facade).flatMap(({facade}) =>
|
|
140
|
+
facade?.publicDeclarations.flatMap((declaration) => declaration.kind == "function"
|
|
141
|
+
? declaration.failures
|
|
142
|
+
: [...declaration.constructor.failures, ...declaration.methods.flatMap(({failures}) => failures)]) ?? []))
|
|
143
|
+
|
|
144
|
+
for (const failure of authority.capabilities.flatMap(({failures}) => failures)) {
|
|
145
|
+
if (!failureNames.has(failure.name)) continue
|
|
146
|
+
const declaration = /** @type {import("../semantic/types.js").ErrorDeclaration} */ (/** @type {unknown} */ (failure))
|
|
147
|
+
|
|
148
|
+
visibleErrorsByName.set(failure.name, declaration)
|
|
149
|
+
visibleErrorsById.set(failure.id, declaration)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
for (const request of requests) {
|
|
154
|
+
const dependency = parsed.get(request.moduleId)
|
|
155
|
+
|
|
156
|
+
if (!dependency) {
|
|
157
|
+
throw new Error(`Dependency '${request.moduleId}' was not parsed before '${source.id}'.`)
|
|
158
|
+
}
|
|
159
|
+
const dependencyHeader = requiredMapValue(headers, request.moduleId)
|
|
160
|
+
const nativeBindingPrefix = request.facade?.nativeModules.find(({identity}) => identity == request.specifier)?.identity ??
|
|
161
|
+
dependencyHeader.nativeName
|
|
162
|
+
const nativeBindings = source.language == "php" && request.namespace
|
|
163
|
+
? (header.bindings ?? []).filter(({nativeName}) => nativeName.startsWith(`${nativeBindingPrefix}\\`))
|
|
164
|
+
: []
|
|
165
|
+
const selectedExports = request.namespace && source.language == "php"
|
|
166
|
+
? dependency.exports.filter(({exportedName}) => nativeBindings.some(({importedName}) => importedName == exportedName))
|
|
167
|
+
: request.namespace
|
|
168
|
+
? dependency.exports
|
|
169
|
+
: dependency.exports.filter(({exportedName}) => exportedName == request.importedName)
|
|
170
|
+
|
|
171
|
+
if (selectedExports.length == 0) {
|
|
172
|
+
semanticFailure(source.language, "UNRESOLVED_IMPORT", request.namespace
|
|
173
|
+
? `Imported module '${request.moduleId}' has no explicitly bound exports.`
|
|
174
|
+
: `Module '${request.moduleId}' does not export '${request.importedName}'.`, request.location)
|
|
175
|
+
}
|
|
176
|
+
for (const exported of selectedExports) {
|
|
177
|
+
const nativeBinding = nativeBindings.find(({importedName}) => importedName == exported.exportedName)
|
|
178
|
+
const localName = nativeBinding?.localName ?? (request.namespace
|
|
179
|
+
? qualifiedImportName(source.language, dependencyHeader.nativeName, exported)
|
|
180
|
+
: request.localName
|
|
181
|
+
)
|
|
182
|
+
const importLocation = nativeBinding?.location ?? request.location
|
|
183
|
+
|
|
184
|
+
if (localNames.has(localName)) {
|
|
185
|
+
semanticFailure(source.language, "DUPLICATE_IMPORT_BINDING", `Duplicate import binding '${localName}'.`, importLocation)
|
|
186
|
+
}
|
|
187
|
+
localNames.add(localName)
|
|
188
|
+
if (nativeBinding && nativeBinding.symbolKind != exported.symbolKind &&
|
|
189
|
+
!(nativeBinding.symbolKind == "record" && exported.symbolKind == "error")) {
|
|
190
|
+
semanticFailure(source.language, "IMPORT_KIND_MISMATCH", `Import '${nativeBinding.importedName}' has the wrong declaration kind.`, importLocation)
|
|
191
|
+
}
|
|
192
|
+
if (request.typeOnly && exported.symbolKind != "record") {
|
|
193
|
+
semanticFailure(source.language, "IMPORT_KIND_MISMATCH", `Type import '${request.importedName}' does not name a record.`, importLocation)
|
|
194
|
+
}
|
|
195
|
+
if (nativeBinding) consumedBindings.add(nativeBinding)
|
|
196
|
+
const declaration = declarationFor(dependency, exported)
|
|
197
|
+
const typeOnly = nativeBinding?.typeOnly ?? request.typeOnly
|
|
198
|
+
|
|
199
|
+
if (exported.symbolKind == "function") {
|
|
200
|
+
visibleFunctions.set(localName, /** @type {import("../semantic/types.js").FunctionDeclaration} */ (declaration))
|
|
201
|
+
visibleCallEffects.set(localName, new Set(callEffectsByDeclarationId.get(exported.declarationId) ?? []))
|
|
202
|
+
}
|
|
203
|
+
else if (exported.symbolKind == "record") {
|
|
204
|
+
const record = /** @type {import("../semantic/types.js").RecordDeclaration} */ (declaration)
|
|
205
|
+
|
|
206
|
+
visibleRecordsByName.set(localName, record)
|
|
207
|
+
if (!typeOnly) visibleValueRecordsByName.set(localName, record)
|
|
208
|
+
visibleRecordsById.set(/** @type {string} */ (record.id), record)
|
|
209
|
+
} else if (exported.symbolKind == "class") {
|
|
210
|
+
const declaration = /** @type {import("../semantic/types.js").ClassDeclaration} */ (declarationFor(dependency, exported))
|
|
211
|
+
|
|
212
|
+
visibleClassesByName.set(localName, declaration)
|
|
213
|
+
visibleClassesById.set(/** @type {string} */ (declaration.id), declaration)
|
|
214
|
+
} else {
|
|
215
|
+
const error = /** @type {import("../semantic/types.js").ErrorDeclaration} */ (declaration)
|
|
216
|
+
|
|
217
|
+
visibleErrorsByName.set(localName, error)
|
|
218
|
+
visibleErrorsById.set(/** @type {string} */ (error.id), error)
|
|
219
|
+
}
|
|
220
|
+
/** @type {Record<string, import("../semantic/types.js").SourceLocation>} */
|
|
221
|
+
const ranges = {
|
|
222
|
+
declaration: request.declarationLocation ?? request.location,
|
|
223
|
+
path: request.pathLocation
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (!request.namespace || nativeBinding || request.facade) {
|
|
227
|
+
ranges.importedName = nativeBinding?.location ?? request.importedNameLocation ?? importLocation
|
|
228
|
+
ranges.localName = nativeBinding?.location ?? request.localNameLocation ?? importLocation
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
imports.push({
|
|
232
|
+
declarationId: exported.declarationId,
|
|
233
|
+
importedName: exported.exportedName,
|
|
234
|
+
kind: "Import",
|
|
235
|
+
localName,
|
|
236
|
+
location: importLocation,
|
|
237
|
+
moduleId: request.moduleId,
|
|
238
|
+
...(request.facade ? {stdlibFacade: {
|
|
239
|
+
facadeIdentity: request.facade.identity,
|
|
240
|
+
facadeVersion: request.facade.version,
|
|
241
|
+
nativeModule: request.specifier,
|
|
242
|
+
nativeSymbol: exported.exportedName,
|
|
243
|
+
schema: /** @type {const} */ ("SemantifoldStdlibFacadeResolution"),
|
|
244
|
+
version: /** @type {const} */ (1)
|
|
245
|
+
}} : {}),
|
|
246
|
+
sourceProvenance: sourceAssociation(importLocation, ranges, sourceId),
|
|
247
|
+
symbolKind: exported.symbolKind,
|
|
248
|
+
typeOnly
|
|
249
|
+
})
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
const unconsumedBinding = (header.bindings ?? []).find((binding) => !consumedBindings.has(binding))
|
|
253
|
+
|
|
254
|
+
if (unconsumedBinding) {
|
|
255
|
+
semanticFailure(source.language, "UNRESOLVED_IMPORT", `Native import '${unconsumedBinding.nativeName}' has no matching required module.`, unconsumedBinding.location)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const frontend = /** @type {(input: object) => import("../semantic/types.js").SemanticModule} */ (
|
|
259
|
+
languageRegistry.resolve(source.language, "frontend"))
|
|
260
|
+
const facadeModules = new Set(source.facade?.requirements.map(({module}) => module) ?? [])
|
|
261
|
+
const moduleCapabilities = requestedAuthority?.capabilities ?? (source.facade?.requirements.length
|
|
262
|
+
? authority?.modules?.filter(({identity}) => facadeModules.has(identity)).flatMap(({capabilities}) => capabilities) ??
|
|
263
|
+
authority?.capabilities
|
|
264
|
+
: undefined)
|
|
265
|
+
const raw = frontend({
|
|
266
|
+
capabilities: moduleCapabilities,
|
|
267
|
+
filename: source.filename,
|
|
268
|
+
language: source.language,
|
|
269
|
+
program: {
|
|
270
|
+
classes: visibleClassesByName,
|
|
271
|
+
functions: visibleFunctions,
|
|
272
|
+
isEntry: source.id == input.entryModule,
|
|
273
|
+
errors: visibleErrorsByName,
|
|
274
|
+
records: visibleRecordsByName,
|
|
275
|
+
valueRecords: visibleValueRecordsByName
|
|
276
|
+
},
|
|
277
|
+
source: source.source
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
if (moduleCapabilities) raw.capabilities = /** @type {import("../semantic/types.js").EffectCapabilityDeclaration[]} */ (moduleCapabilities)
|
|
281
|
+
if ((raw.classes?.length ?? 0) > 0 && !source.facade?.publicDeclarations.some(({kind}) => kind == "class")) {
|
|
282
|
+
semanticFailure(source.language, "UNSUPPORTED_SYNTAX",
|
|
283
|
+
"Task 033 reference classes are not supported by the Task 010 semantic program profile.",
|
|
284
|
+
raw.classes?.[0]?.location ?? raw.location)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (source.id == input.entryModule && raw.entryPoint.body.statements.length == 0) {
|
|
288
|
+
semanticFailure(source.language, "INVALID_ENTRY_MODULE", `Selected entry module '${source.id}' has no executable statements.`, raw.entryPoint.location)
|
|
289
|
+
}
|
|
290
|
+
try {
|
|
291
|
+
validateParsedModule(raw, source.language, {
|
|
292
|
+
callEffects: visibleCallEffects,
|
|
293
|
+
errors: visibleErrorsById,
|
|
294
|
+
functions: visibleFunctions,
|
|
295
|
+
records: visibleRecordsById,
|
|
296
|
+
classes: visibleClassesById
|
|
297
|
+
})
|
|
298
|
+
} catch (error) {
|
|
299
|
+
const task037 = source.language == "ruby" && header.imports.some(({facade}) => facade?.identity.startsWith("semantifold.task037."))
|
|
300
|
+
|
|
301
|
+
if (task037 && error instanceof SemantifoldDiagnostic && error.code == "UNKNOWN_METHOD" &&
|
|
302
|
+
error.detail.startsWith("Class 'TCPSocket'")) {
|
|
303
|
+
facadeFailure("ruby", "STDLIB_FACADE_MEMBER_UNSUPPORTED", error.detail, error.location)
|
|
304
|
+
}
|
|
305
|
+
throw error
|
|
306
|
+
}
|
|
307
|
+
rekeyDeclarations(raw, source.id)
|
|
308
|
+
const localCallEffects = moduleUncheckedErrorEffects(raw, {callEffects: visibleCallEffects, functions: visibleFunctions})
|
|
309
|
+
|
|
310
|
+
for (const declaration of raw.functions) {
|
|
311
|
+
callEffectsByDeclarationId.set(/** @type {string} */ (declaration.id), new Set(localCallEffects.get(declaration.name) ?? []))
|
|
312
|
+
}
|
|
313
|
+
annotateParsedModule(raw, source)
|
|
314
|
+
rebaseParsedModuleProvenance(raw, registeredSources, sourceId)
|
|
315
|
+
|
|
316
|
+
/** @type {Map<string, {declaration: import("../semantic/types.js").FunctionDeclaration | import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").ErrorDeclaration, symbolKind: import("../semantic/types.js").SemanticDeclarationKind}>} */
|
|
317
|
+
const declarationsByName = new Map()
|
|
318
|
+
|
|
319
|
+
for (const declaration of raw.records ?? []) declarationsByName.set(declaration.name, {declaration, symbolKind: "record"})
|
|
320
|
+
for (const declaration of raw.classes ?? []) declarationsByName.set(declaration.name, {declaration, symbolKind: "class"})
|
|
321
|
+
for (const declaration of raw.errors ?? []) declarationsByName.set(declaration.name, {declaration, symbolKind: "error"})
|
|
322
|
+
for (const declaration of raw.functions) declarationsByName.set(declaration.name, {declaration, symbolKind: "function"})
|
|
323
|
+
/** @type {import("../semantic/types.js").SemanticExport[]} */
|
|
324
|
+
const exports = header.exports.map((request) => {
|
|
325
|
+
const resolved = declarationsByName.get(request.localName)
|
|
326
|
+
|
|
327
|
+
if (!resolved) semanticFailure(source.language, "UNRESOLVED_EXPORT", `Unknown exported declaration '${request.localName}'.`, request.location)
|
|
328
|
+
if (request.typeOnly && resolved.symbolKind != "record") {
|
|
329
|
+
semanticFailure(source.language, "EXPORT_KIND_MISMATCH", `Type export '${request.exportedName}' does not name a record.`, request.location)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return {
|
|
333
|
+
declarationId: /** @type {string} */ (resolved.declaration.id),
|
|
334
|
+
exportedName: request.exportedName,
|
|
335
|
+
kind: /** @type {const} */ ("Export"),
|
|
336
|
+
location: request.location,
|
|
337
|
+
sourceProvenance: sourceAssociation(request.location, {
|
|
338
|
+
declaration: request.declarationLocation ?? request.location,
|
|
339
|
+
exportedName: request.exportedNameLocation ?? request.location,
|
|
340
|
+
localName: request.localNameLocation ?? request.location
|
|
341
|
+
}, sourceId),
|
|
342
|
+
symbolKind: resolved.symbolKind
|
|
343
|
+
}
|
|
344
|
+
})
|
|
345
|
+
const exportedNames = new Set()
|
|
346
|
+
|
|
347
|
+
for (const exported of exports) {
|
|
348
|
+
if (exportedNames.has(exported.exportedName)) {
|
|
349
|
+
semanticFailure(source.language, "DUPLICATE_EXPORT", `Duplicate export '${exported.exportedName}'.`, exported.location)
|
|
350
|
+
}
|
|
351
|
+
exportedNames.add(exported.exportedName)
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const {entryPoint, ...moduleWithoutEntry} = raw
|
|
355
|
+
const module = /** @type {import("../semantic/types.js").SemanticProgramModule} */ ({
|
|
356
|
+
...moduleWithoutEntry,
|
|
357
|
+
...(source.id == input.entryModule ? {entryPoint} : {}),
|
|
358
|
+
exports,
|
|
359
|
+
id: source.id,
|
|
360
|
+
imports,
|
|
361
|
+
...(source.facade ? {stdlibFacade: {identity: source.facade.identity, version: source.facade.version}} : {}),
|
|
362
|
+
sourceFilename: source.filename
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
if (source.facade) validateParsedFacade(source.facade, module)
|
|
366
|
+
|
|
367
|
+
parsed.set(source.id, module)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return {
|
|
371
|
+
entryModule: input.entryModule,
|
|
372
|
+
kind: "Program",
|
|
373
|
+
modules: orderedIds.map((id) => /** @type {import("../semantic/types.js").SemanticProgramModule} */ (parsed.get(id))),
|
|
374
|
+
...(requestedAuthority ? {
|
|
375
|
+
stdlibContract: {
|
|
376
|
+
identity: requestedAuthority.id,
|
|
377
|
+
...(requestedAuthority.contractVersion !== undefined ? {contractVersion: requestedAuthority.contractVersion} : {})
|
|
378
|
+
}
|
|
379
|
+
} : facadeAuthority && "contracts" in facadeAuthority ? {stdlibContracts: facadeAuthority.contracts}
|
|
380
|
+
: facadeAuthority ? {stdlibContract: facadeAuthority.contract} : {}),
|
|
381
|
+
...(facadeRecords.length > 0 ? {stdlibFacades: stdlibFacadeProgramDescriptor(applicationSources[0].language, facadeRecords)} : {}),
|
|
382
|
+
sources: registeredSources
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Resolves parser-owned native module and symbol evidence to facade roots.
|
|
388
|
+
* Mutating the private header requests keeps the proof attached to the exact import node.
|
|
389
|
+
* @param {ProgramSource[]} sources - Caller-owned sources.
|
|
390
|
+
* @param {Map<string, ProgramHeader>} headers - Parser-owned source headers.
|
|
391
|
+
* @returns {import("../stdlib-facades.js").StdlibFacadeRecord[]} Selected facade roots.
|
|
392
|
+
*/
|
|
393
|
+
function resolveFacadeImports(sources, headers) {
|
|
394
|
+
/** @type {import("../stdlib-facades.js").StdlibFacadeRecord[]} */
|
|
395
|
+
const roots = []
|
|
396
|
+
|
|
397
|
+
for (const source of sources) {
|
|
398
|
+
const header = requiredMapValue(headers, source.id)
|
|
399
|
+
|
|
400
|
+
if (source.language == "ruby") resolveRubyTask037Facades(source, header, roots)
|
|
401
|
+
|
|
402
|
+
if (source.language == "php") {
|
|
403
|
+
for (const binding of header.bindings ?? []) {
|
|
404
|
+
const parts = binding.nativeName.split("\\")
|
|
405
|
+
const symbol = /** @type {string} */ (parts.pop())
|
|
406
|
+
const nativeModule = parts.join("\\")
|
|
407
|
+
|
|
408
|
+
if (!nativeModule.startsWith("Semantifold\\Task036\\")) continue
|
|
409
|
+
const facade = resolveFacadeAt(source, nativeModule, symbol, binding.location)
|
|
410
|
+
|
|
411
|
+
header.imports.push({
|
|
412
|
+
declarationLocation: binding.location,
|
|
413
|
+
facade,
|
|
414
|
+
importedName: "*",
|
|
415
|
+
importedNameLocation: binding.location,
|
|
416
|
+
localName: "",
|
|
417
|
+
localNameLocation: binding.location,
|
|
418
|
+
location: binding.location,
|
|
419
|
+
namespace: true,
|
|
420
|
+
pathLocation: binding.location,
|
|
421
|
+
specifier: nativeModule,
|
|
422
|
+
stdlibCandidate: true,
|
|
423
|
+
typeOnly: false
|
|
424
|
+
})
|
|
425
|
+
roots.push(facade)
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
for (const request of header.imports) {
|
|
430
|
+
const javaCandidate = source.language == "java" && request.specifier.startsWith("semantifold.task036.")
|
|
431
|
+
|
|
432
|
+
if (!request.stdlibCandidate && !javaCandidate) continue
|
|
433
|
+
if (request.facade) continue
|
|
434
|
+
if (/\.(?:bundle|dll|node|so)$/iu.test(request.specifier)) {
|
|
435
|
+
facadeFailure(source.language, "STDLIB_FACADE_NATIVE_EXTENSION_UNSUPPORTED",
|
|
436
|
+
"Native-extension loading cannot participate in a portable stdlib facade.", request.pathLocation)
|
|
437
|
+
}
|
|
438
|
+
const supplied = [...headers.entries()].find(([, candidate]) => candidate.nativeName == request.specifier)
|
|
439
|
+
|
|
440
|
+
if (supplied) {
|
|
441
|
+
facadeFailure(source.language, "STDLIB_FACADE_SHADOWED",
|
|
442
|
+
`Caller-supplied module '${supplied[0]}' replaces the catalogued native facade identity '${request.specifier}'.`, request.pathLocation)
|
|
443
|
+
}
|
|
444
|
+
const facade = request.namespace
|
|
445
|
+
? resolveFacadeModuleAt(source, request.specifier, request.pathLocation)
|
|
446
|
+
: resolveFacadeAt(source, request.specifier, request.importedName, request.importedNameLocation ?? request.location)
|
|
447
|
+
|
|
448
|
+
request.facade = facade
|
|
449
|
+
roots.push(facade)
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return roots
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Resolves exact Task 037 Ruby references under a literal socket activation edge.
|
|
458
|
+
* @param {ProgramSource} source - Importing Ruby source.
|
|
459
|
+
* @param {ProgramHeader} header - Parser-owned Ruby header.
|
|
460
|
+
* @param {import("../stdlib-facades.js").StdlibFacadeRecord[]} roots - Selected facade roots.
|
|
461
|
+
* @returns {void}
|
|
462
|
+
*/
|
|
463
|
+
function resolveRubyTask037Facades(source, header, roots) {
|
|
464
|
+
const activationIndex = header.imports.findIndex(({specifier, stdlibCandidate}) => stdlibCandidate && specifier == "socket")
|
|
465
|
+
|
|
466
|
+
if (activationIndex < 0) return
|
|
467
|
+
const activation = /** @type {ProgramImportRequest} */ (header.imports.splice(activationIndex, 1)[0])
|
|
468
|
+
const bindings = new Set()
|
|
469
|
+
|
|
470
|
+
for (const reference of header.nativeReferences ?? []) {
|
|
471
|
+
if (reference.nativeModule != "socket" && reference.nativeModule != "ruby:Kernel") continue
|
|
472
|
+
const localName = reference.form == "receiver-call" ? "Kernel.puts" : reference.symbol
|
|
473
|
+
|
|
474
|
+
if (bindings.has(localName)) continue
|
|
475
|
+
bindings.add(localName)
|
|
476
|
+
const facade = resolveFacadeAt(source, reference.nativeModule, reference.symbol, reference.location)
|
|
477
|
+
|
|
478
|
+
header.imports.push({
|
|
479
|
+
declarationLocation: reference.location,
|
|
480
|
+
facade,
|
|
481
|
+
importedName: reference.symbol,
|
|
482
|
+
importedNameLocation: reference.location,
|
|
483
|
+
localName,
|
|
484
|
+
localNameLocation: reference.location,
|
|
485
|
+
location: reference.location,
|
|
486
|
+
pathLocation: activation.pathLocation,
|
|
487
|
+
specifier: reference.nativeModule,
|
|
488
|
+
stdlibCandidate: true,
|
|
489
|
+
typeOnly: false
|
|
490
|
+
})
|
|
491
|
+
roots.push(facade)
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Resolves one exact native module and symbol while preserving parser location.
|
|
497
|
+
* @param {ProgramSource} source - Importing source.
|
|
498
|
+
* @param {string} nativeModule - Parser-proved native module identity.
|
|
499
|
+
* @param {string} symbol - Parser-proved native symbol.
|
|
500
|
+
* @param {import("../semantic/types.js").SourceLocation} location - Identity evidence location.
|
|
501
|
+
* @returns {import("../stdlib-facades.js").StdlibFacadeRecord} Located exact native facade resolution.
|
|
502
|
+
*/
|
|
503
|
+
function resolveFacadeAt(source, nativeModule, symbol, location) {
|
|
504
|
+
try {
|
|
505
|
+
return stdlibFacadeRegistry.resolveFacade(source.language, nativeModule, symbol)
|
|
506
|
+
} catch (error) {
|
|
507
|
+
if (error instanceof SemantifoldDiagnostic) facadeFailure(source.language, error.code, error.detail, location)
|
|
508
|
+
throw error
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Resolves one exact namespace-style native module while preserving parser location.
|
|
514
|
+
* @param {ProgramSource} source - Importing source.
|
|
515
|
+
* @param {string} nativeModule - Parser-proved native module identity.
|
|
516
|
+
* @param {import("../semantic/types.js").SourceLocation} location - Identity evidence location.
|
|
517
|
+
* @returns {import("../stdlib-facades.js").StdlibFacadeRecord} Located exact native facade-module resolution.
|
|
518
|
+
*/
|
|
519
|
+
function resolveFacadeModuleAt(source, nativeModule, location) {
|
|
520
|
+
try {
|
|
521
|
+
return stdlibFacadeRegistry.resolveModule(source.language, nativeModule)
|
|
522
|
+
} catch (error) {
|
|
523
|
+
if (error instanceof SemantifoldDiagnostic) facadeFailure(source.language, error.code, error.detail, location)
|
|
524
|
+
throw error
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Rejects caller capture of compiler-owned module/source/native names and imported bindings.
|
|
530
|
+
* @param {ProgramSource[]} applicationSources - Caller-owned sources.
|
|
531
|
+
* @param {Map<string, ProgramHeader>} headers - Caller parser headers.
|
|
532
|
+
* @param {ProgramSource[]} facadeSources - Selected compiler-owned facade sources.
|
|
533
|
+
* @returns {void}
|
|
534
|
+
*/
|
|
535
|
+
function validateFacadeCollisions(applicationSources, headers, facadeSources) {
|
|
536
|
+
const facadeIds = new Set(facadeSources.map(({id}) => id))
|
|
537
|
+
const facadeFilenames = new Set(facadeSources.map(({filename}) => filename))
|
|
538
|
+
const facadeNativeNames = new Set(facadeSources.map((source) => inspectSource(source).nativeName).filter(Boolean))
|
|
539
|
+
|
|
540
|
+
for (const source of applicationSources) {
|
|
541
|
+
const header = requiredMapValue(headers, source.id)
|
|
542
|
+
|
|
543
|
+
if (facadeIds.has(source.id) || facadeFilenames.has(source.filename)) {
|
|
544
|
+
facadeFailure(source.language, "STDLIB_FACADE_NAME_COLLISION",
|
|
545
|
+
`Caller module '${source.id}' collides with a compiler-owned facade module or source path.`, moduleLocation(source.filename, source.source))
|
|
546
|
+
}
|
|
547
|
+
if (header.nativeName && facadeNativeNames.has(header.nativeName)) {
|
|
548
|
+
facadeFailure(source.language, "STDLIB_FACADE_REOPENED",
|
|
549
|
+
`Caller source reopens the compiler-owned facade native identity '${header.nativeName}'.`, moduleLocation(source.filename, source.source))
|
|
550
|
+
}
|
|
551
|
+
const importedBindings = new Set(header.imports.filter(({facade}) => facade && !facade.publicDeclarations.some(({name}) => name == ""))
|
|
552
|
+
.flatMap((request) => request.namespace && source.language == "php"
|
|
553
|
+
? (header.bindings ?? []).filter(({nativeName}) => nativeName.startsWith(`${request.specifier}\\`)).map(({localName}) => localName)
|
|
554
|
+
: request.namespace ? [] : [request.localName]))
|
|
555
|
+
const shadowed = header.exports.find(({localName}) => importedBindings.has(localName))
|
|
556
|
+
|
|
557
|
+
if (shadowed) facadeFailure(source.language, "STDLIB_FACADE_SHADOWED",
|
|
558
|
+
`Local declaration '${shadowed.localName}' shadows a parser-proved stdlib facade binding.`, shadowed.location)
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Validates a parsed compiler-owned facade against its registry declaration and canonical-only requirements.
|
|
564
|
+
* @param {import("../stdlib-facades.js").StdlibFacadeRecord} facade - Selected facade record.
|
|
565
|
+
* @param {import("../semantic/types.js").SemanticProgramModule} module - Parsed semantic module.
|
|
566
|
+
* @returns {void}
|
|
567
|
+
*/
|
|
568
|
+
function validateParsedFacade(facade, module) {
|
|
569
|
+
const exported = new Map(module.exports.map((item) => [item.exportedName, item]))
|
|
570
|
+
|
|
571
|
+
for (const declaration of facade.publicDeclarations) {
|
|
572
|
+
if (declaration.kind == "class") {
|
|
573
|
+
validateParsedFacadeClass(facade, module, declaration, exported.get(declaration.name))
|
|
574
|
+
continue
|
|
575
|
+
}
|
|
576
|
+
const exportedDeclaration = exported.get(declaration.name)
|
|
577
|
+
const parsed = exportedDeclaration ? module.functions.find(({id}) => id == exportedDeclaration.declarationId) : undefined
|
|
578
|
+
const signatureMatches = parsed && parsed.parameters.length == declaration.parameters.length &&
|
|
579
|
+
parsed.parameters.every((parameter, index) => parameter.name == declaration.parameters[index].name &&
|
|
580
|
+
semanticTypeName(parameter.type) == declaration.parameters[index].type) &&
|
|
581
|
+
semanticTypeName(parsed.returnType) == declaration.returnType
|
|
582
|
+
|
|
583
|
+
if (!signatureMatches) facadeFailure(facade.language, "STDLIB_FACADE_DECLARATION_MISMATCH",
|
|
584
|
+
`Facade '${facade.identity}' executable source does not match public declaration '${declaration.name}'.`, parsed?.location ?? module.location)
|
|
585
|
+
}
|
|
586
|
+
const dependencyIds = new Set(facade.dependencies.map((dependency) =>
|
|
587
|
+
stdlibFacadeRegistry.resolveIdentity(dependency.identity, facade.language, dependency.range).source.id))
|
|
588
|
+
|
|
589
|
+
if (module.imports.some(({moduleId}) => !dependencyIds.has(moduleId)) ||
|
|
590
|
+
[...dependencyIds].some((moduleId) => !module.imports.some((imported) => imported.moduleId == moduleId))) {
|
|
591
|
+
facadeFailure(facade.language, "STDLIB_FACADE_DECLARATION_MISMATCH",
|
|
592
|
+
`Facade '${facade.identity}' executable imports do not match its declared dependencies.`, module.location)
|
|
593
|
+
}
|
|
594
|
+
const allowedOperations = new Set(facade.requirements.flatMap(({operations}) => operations))
|
|
595
|
+
const operationNames = new Map((module.capabilities ?? []).flatMap(({operations}) => operations.map(({id, name}) => [id, name])))
|
|
596
|
+
const usedOperations = collectEffectOperations(module, operationNames)
|
|
597
|
+
|
|
598
|
+
if ([...usedOperations].some((operation) => !allowedOperations.has(operation)) ||
|
|
599
|
+
[...allowedOperations].some((operation) => !usedOperations.has(operation))) {
|
|
600
|
+
facadeFailure(facade.language, "STDLIB_FACADE_AUTHORITY_FORGED",
|
|
601
|
+
`Facade '${facade.identity}' canonical calls do not match its declared requirements.`, module.location)
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Verifies one executable compiler-owned facade class against its closed descriptor.
|
|
607
|
+
* @param {import("../stdlib-facades.js").StdlibFacadeRecord} facade - Owning facade.
|
|
608
|
+
* @param {import("../semantic/types.js").SemanticProgramModule} module - Parsed facade module.
|
|
609
|
+
* @param {import("../stdlib-facades.js").StdlibFacadeClassDeclaration} descriptor - Registry descriptor.
|
|
610
|
+
* @param {import("../semantic/types.js").SemanticExport | undefined} exported - Matching export evidence.
|
|
611
|
+
* @returns {void}
|
|
612
|
+
*/
|
|
613
|
+
function validateParsedFacadeClass(facade, module, descriptor, exported) {
|
|
614
|
+
const parsed = exported?.symbolKind == "class"
|
|
615
|
+
? module.classes?.find(({id}) => id == exported.declarationId) : undefined
|
|
616
|
+
const failureNames = new Map((module.capabilities ?? []).flatMap(({failures}) => failures.map(({id, name}) => [id, name])))
|
|
617
|
+
const constructorFailures = parsed?.constructor.failureIds?.map((id) => failureNames.get(id)) ?? []
|
|
618
|
+
const constructorMatches = parsed?.name == descriptor.name && parsed.ownership?.kind == "ownedResource" &&
|
|
619
|
+
parsed.constructor.effects?.join(",") == descriptor.constructor.effects.join(",") &&
|
|
620
|
+
constructorFailures.join(",") == descriptor.constructor.failures.join(",") &&
|
|
621
|
+
parsed.constructor.parameters.length == descriptor.constructor.parameters.length &&
|
|
622
|
+
parsed.constructor.parameters.every((parameter, index) => parameter.name == descriptor.constructor.parameters[index].name &&
|
|
623
|
+
semanticTypeName(parameter.type) == descriptor.constructor.parameters[index].type)
|
|
624
|
+
const methodsMatch = parsed?.methods.length == descriptor.methods.length && descriptor.methods.every((expected, index) => {
|
|
625
|
+
const method = parsed?.methods[index]
|
|
626
|
+
const actualFailures = method?.failureIds?.map((id) => failureNames.get(id)) ?? []
|
|
627
|
+
const operation = method?.body.statements.flatMap((statement) => {
|
|
628
|
+
const expression = statement.kind == "ExpressionStatement" || statement.kind == "ReturnStatement"
|
|
629
|
+
? statement.expression : statement.kind == "LocalDeclaration" ? statement.initializer : undefined
|
|
630
|
+
|
|
631
|
+
return expression?.kind == "EffectCallExpression" ? [expression] : []
|
|
632
|
+
})[0]
|
|
633
|
+
const flow = operation?.resolution.resourceFlow
|
|
634
|
+
const terminalFailureId = flow?.kind == "borrow" || flow?.kind == "close" ? flow.terminalFailureId : undefined
|
|
635
|
+
|
|
636
|
+
if (terminalFailureId) actualFailures.push(failureNames.get(terminalFailureId))
|
|
637
|
+
return method?.name == expected.name && method.effects?.join(",") == expected.effects.join(",") &&
|
|
638
|
+
actualFailures.join(",") == expected.failures.join(",") && method.parameters.length == expected.parameters.length &&
|
|
639
|
+
method.parameters.every((parameter, parameterIndex) =>
|
|
640
|
+
parameter.name == expected.parameters[parameterIndex].name &&
|
|
641
|
+
semanticTypeName(parameter.type) == expected.parameters[parameterIndex].type) &&
|
|
642
|
+
semanticTypeName(method.returnType) == expected.returnType
|
|
643
|
+
})
|
|
644
|
+
|
|
645
|
+
if (!constructorMatches || !methodsMatch) {
|
|
646
|
+
facadeFailure(facade.language, "STDLIB_FACADE_DECLARATION_MISMATCH",
|
|
647
|
+
`Facade '${facade.identity}' executable source does not match public class '${descriptor.name}'.`, parsed?.location ?? module.location)
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Collects canonical operation names reached in one facade module.
|
|
653
|
+
* @param {import("../semantic/types.js").SemanticProgramModule} module - Parsed facade module.
|
|
654
|
+
* @param {Map<string, string>} operationNames - Canonical names by operation identity.
|
|
655
|
+
* @returns {Set<string>} Used operation names.
|
|
656
|
+
*/
|
|
657
|
+
function collectEffectOperations(module, operationNames) {
|
|
658
|
+
const used = new Set()
|
|
659
|
+
const seen = new WeakSet()
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Visits one semantic subtree without following authority or provenance graphs.
|
|
663
|
+
* @param {unknown} value - Semantic subtree.
|
|
664
|
+
* @returns {void}
|
|
665
|
+
*/
|
|
666
|
+
function visit(value) {
|
|
667
|
+
if (!value || typeof value != "object" || seen.has(value)) return
|
|
668
|
+
seen.add(value)
|
|
669
|
+
if (!Array.isArray(value) && Reflect.get(value, "kind") == "EffectCallExpression") {
|
|
670
|
+
const name = operationNames.get(Reflect.get(value, "operation"))
|
|
671
|
+
|
|
672
|
+
if (name) used.add(name)
|
|
673
|
+
}
|
|
674
|
+
for (const [key, child] of Object.entries(value)) {
|
|
675
|
+
if (key != "capabilities" && key != "location" && key != "provenance" && key != "sourceProvenance") visit(child)
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
visit(module)
|
|
679
|
+
return used
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Converts one parsed semantic type to the facade declaration vocabulary.
|
|
684
|
+
* @param {import("../semantic/types.js").SemanticFunctionReturnType} type - Parsed type.
|
|
685
|
+
* @returns {string} Closed canonical public type name.
|
|
686
|
+
*/
|
|
687
|
+
function semanticTypeName(type) {
|
|
688
|
+
if (type.kind == "TypeReference") return type.name
|
|
689
|
+
return type.kind == "OptionalType" && type.valueType.kind == "TypeReference"
|
|
690
|
+
? `optional:${type.valueType.name}` : "unsupported"
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Throws one stable located facade diagnostic.
|
|
695
|
+
* @param {string} language - Source language.
|
|
696
|
+
* @param {string} code - Stable diagnostic code.
|
|
697
|
+
* @param {string} message - Diagnostic detail.
|
|
698
|
+
* @param {import("../semantic/types.js").SourceLocation | undefined} location - Parser-owned location.
|
|
699
|
+
* @returns {never} Always throws.
|
|
700
|
+
*/
|
|
701
|
+
function facadeFailure(language, code, message, location) {
|
|
702
|
+
throw new SemantifoldDiagnostic({code, language, location, message})
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* Rejects source-profile identities whose lookup or reopening would depend on caller order.
|
|
707
|
+
* @param {ProgramSource[]} sources - Complete validated source set.
|
|
708
|
+
* @param {Map<string, ProgramHeader>} headers - Parser-owned module headers.
|
|
709
|
+
* @returns {void}
|
|
710
|
+
*/
|
|
711
|
+
function validateNativeModuleIdentities(sources, headers) {
|
|
712
|
+
if (!sources.every(({language}) => language == "java") && !sources.every(({language}) => language == "ruby")) return
|
|
713
|
+
const ordered = [...sources].sort((left, right) => left.filename.localeCompare(right.filename, "en-US") ||
|
|
714
|
+
left.id.localeCompare(right.id, "en-US"))
|
|
715
|
+
const owners = new Map()
|
|
716
|
+
|
|
717
|
+
for (const source of ordered) {
|
|
718
|
+
const nativeName = requiredMapValue(headers, source.id).nativeName
|
|
719
|
+
|
|
720
|
+
if (!nativeName) continue
|
|
721
|
+
const earlier = owners.get(nativeName)
|
|
722
|
+
|
|
723
|
+
if (earlier) {
|
|
724
|
+
const label = source.language == "java" ? "Java" : "Ruby"
|
|
725
|
+
|
|
726
|
+
semanticFailure(source.language, "DUPLICATE_MODULE",
|
|
727
|
+
`Duplicate ${label} native module identity '${nativeName}' in '${earlier.filename}' and '${source.filename}'.`,
|
|
728
|
+
moduleLocation(source.filename, source.source))
|
|
729
|
+
}
|
|
730
|
+
owners.set(nativeName, source)
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* Validates and snapshots one caller-supplied program source descriptor.
|
|
736
|
+
* @param {unknown} candidate - Candidate source descriptor.
|
|
737
|
+
* @param {number} index - Source index.
|
|
738
|
+
* @returns {ProgramSource} Validated source descriptor.
|
|
739
|
+
*/
|
|
740
|
+
function validateSource(candidate, index) {
|
|
741
|
+
if (!isPlainObject(candidate)) {
|
|
742
|
+
return invalidProgram(`Program source ${index} requires a stable module ID, safe filename, language, and source text.`)
|
|
743
|
+
}
|
|
744
|
+
if (!isPlainObject(candidate) || typeof candidate.id != "string" || !moduleIdPattern.test(candidate.id) ||
|
|
745
|
+
typeof candidate.filename != "string" || !isSafeSourcePath(candidate.filename) ||
|
|
746
|
+
typeof candidate.source != "string" || typeof candidate.language != "string") {
|
|
747
|
+
invalidProgram(`Program source ${index} requires a stable module ID, safe filename, language, and source text.`)
|
|
748
|
+
}
|
|
749
|
+
languageRegistry.record(candidate.language)
|
|
750
|
+
if (!programLanguages.has(candidate.language)) unsupportedRole(candidate.language, "multi-file frontend")
|
|
751
|
+
|
|
752
|
+
return /** @type {ProgramSource} */ ({
|
|
753
|
+
filename: candidate.filename,
|
|
754
|
+
id: candidate.id,
|
|
755
|
+
language: candidate.language,
|
|
756
|
+
ownership: "application",
|
|
757
|
+
source: candidate.source
|
|
758
|
+
})
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Dispatches parser-owned module header inspection for one cohort source.
|
|
763
|
+
* @param {{filename: string, language: import("../semantic/types.js").SemanticLanguage, source: string}} source - Validated source.
|
|
764
|
+
* @returns {ProgramHeader} Parser-owned import/export header.
|
|
765
|
+
*/
|
|
766
|
+
function inspectSource(source) {
|
|
767
|
+
if (source.language == "javascript" || source.language == "typescript") {
|
|
768
|
+
return inspectJavaScriptTypeScriptModule(/** @type {{filename: string, language: "javascript" | "typescript", source: string}} */ (source))
|
|
769
|
+
}
|
|
770
|
+
if (source.language == "ruby") {
|
|
771
|
+
return inspectRubyModule(/** @type {{filename: string, language: "ruby", source: string}} */ (source))
|
|
772
|
+
}
|
|
773
|
+
if (source.language == "php") {
|
|
774
|
+
return inspectPhpModule(/** @type {{filename: string, language: "php", source: string}} */ (source))
|
|
775
|
+
}
|
|
776
|
+
if (source.language == "java") {
|
|
777
|
+
return inspectJavaModule(/** @type {{filename: string, language: "java", source: string}} */ (source))
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
return unsupportedRole(source.language, "multi-file frontend")
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
* Resolves only an explicit relative ESM specifier against caller-supplied filenames.
|
|
785
|
+
* @param {{filename: string, language: import("../semantic/types.js").SemanticLanguage}} source - Importing source.
|
|
786
|
+
* @param {string} specifier - Parser-owned relative specifier.
|
|
787
|
+
* @param {import("../semantic/types.js").SourceLocation} location - Specifier location.
|
|
788
|
+
* @param {Map<string, ProgramSource>} sources - Sources by exact filename.
|
|
789
|
+
* @param {Map<string, ProgramHeader>} headers - Parser-owned module headers.
|
|
790
|
+
* @returns {string} Resolved module identity.
|
|
791
|
+
*/
|
|
792
|
+
function resolveImportModule(source, specifier, location, sources, headers) {
|
|
793
|
+
if (source.language == "java") {
|
|
794
|
+
const matched = [...headers.entries()].find(([, header]) => header.nativeName == specifier)
|
|
795
|
+
|
|
796
|
+
if (!matched) semanticFailure(source.language, "UNRESOLVED_MODULE", `Import '${specifier}' does not identify a supplied source.`, location)
|
|
797
|
+
|
|
798
|
+
return matched[0]
|
|
799
|
+
}
|
|
800
|
+
const directory = source.filename.split("/").slice(0, -1)
|
|
801
|
+
const parts = [...directory]
|
|
802
|
+
|
|
803
|
+
for (const part of specifier.split("/")) {
|
|
804
|
+
if (part == "" || part == ".") continue
|
|
805
|
+
if (part == "..") {
|
|
806
|
+
if (parts.length == 0) semanticFailure(source.language, "UNSAFE_MODULE_PATH", `Import '${specifier}' escapes the supplied source root.`, location)
|
|
807
|
+
parts.pop()
|
|
808
|
+
} else parts.push(part)
|
|
809
|
+
}
|
|
810
|
+
let exact = parts.join("/")
|
|
811
|
+
|
|
812
|
+
if (source.language == "ruby" && !exact.endsWith(".rb")) exact += ".rb"
|
|
813
|
+
const candidates = [exact]
|
|
814
|
+
|
|
815
|
+
if (source.language == "typescript" && exact.endsWith(".js")) candidates.push(`${exact.slice(0, -3)}.ts`)
|
|
816
|
+
const matched = candidates.flatMap((filename) => sources.has(filename) ? [sources.get(filename)] : [])[0]
|
|
817
|
+
|
|
818
|
+
if (!matched) semanticFailure(source.language, "UNRESOLVED_MODULE", `Import '${specifier}' does not identify a supplied source.`, location)
|
|
819
|
+
|
|
820
|
+
return matched.id
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
/**
|
|
824
|
+
* Builds the source-profile spelling used to resolve one namespace-qualified import.
|
|
825
|
+
* @param {import("../semantic/types.js").SemanticLanguage} language - Importing source language.
|
|
826
|
+
* @param {string | undefined} nativeName - Dependency namespace/module name.
|
|
827
|
+
* @param {import("../semantic/types.js").SemanticExport} exported - Imported export.
|
|
828
|
+
* @returns {string} Source-profile binding spelling.
|
|
829
|
+
*/
|
|
830
|
+
function qualifiedImportName(language, nativeName, exported) {
|
|
831
|
+
if (!nativeName) throw new Error("Namespace import dependency has no parser-owned native name.")
|
|
832
|
+
if (language == "ruby") return `${nativeName}${exported.symbolKind == "function" ? "." : "::"}${exported.exportedName}`
|
|
833
|
+
if (language == "java") {
|
|
834
|
+
const className = nativeName.split(".").at(-1)
|
|
835
|
+
|
|
836
|
+
if (!className) throw new Error("Java import dependency has no class name.")
|
|
837
|
+
|
|
838
|
+
return exported.symbolKind == "function" ? `${className}.${exported.exportedName}` : className
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
throw new Error(`Unsupported namespace import profile '${language}'.`)
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* Sorts the explicit module graph in stable dependency-first order.
|
|
846
|
+
* @param {ProgramSource[]} sources - Validated sources.
|
|
847
|
+
* @param {Map<string, {source: ProgramSource, requests: {moduleId: string, location: import("../semantic/types.js").SourceLocation}[]}>} graph - Resolved graph.
|
|
848
|
+
* @returns {string[]} Dependency-first module identities.
|
|
849
|
+
*/
|
|
850
|
+
function topologicalOrder(sources, graph) {
|
|
851
|
+
const complete = new Set()
|
|
852
|
+
const active = new Set()
|
|
853
|
+
/** @type {string[]} */
|
|
854
|
+
const result = []
|
|
855
|
+
|
|
856
|
+
/**
|
|
857
|
+
* Visits one module and its dependencies.
|
|
858
|
+
* @param {string} id - Module identity.
|
|
859
|
+
*/
|
|
860
|
+
function visit(id) {
|
|
861
|
+
if (complete.has(id)) return
|
|
862
|
+
if (active.has(id)) {
|
|
863
|
+
const edge = requiredMapValue(graph, id).requests[0]
|
|
864
|
+
|
|
865
|
+
semanticFailure(requiredMapValue(graph, id).source.language, "IMPORT_CYCLE", `Import cycle includes module '${id}'.`, edge?.location)
|
|
866
|
+
}
|
|
867
|
+
active.add(id)
|
|
868
|
+
for (const request of requiredMapValue(graph, id).requests) visit(request.moduleId)
|
|
869
|
+
active.delete(id)
|
|
870
|
+
complete.add(id)
|
|
871
|
+
result.push(id)
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
for (const source of sources) visit(source.id)
|
|
875
|
+
|
|
876
|
+
return result
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
/**
|
|
880
|
+
* Looks up one already-resolved exported declaration.
|
|
881
|
+
* @param {import("../semantic/types.js").SemanticProgramModule} module - Exporting module.
|
|
882
|
+
* @param {import("../semantic/types.js").SemanticExport} exported - Resolved export.
|
|
883
|
+
* @returns {import("../semantic/types.js").FunctionDeclaration | import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").ErrorDeclaration} Declaration.
|
|
884
|
+
*/
|
|
885
|
+
function declarationFor(module, exported) {
|
|
886
|
+
const declarations = exported.symbolKind == "function" ? module.functions :
|
|
887
|
+
exported.symbolKind == "record" ? module.records ?? [] : exported.symbolKind == "class" ? module.classes ?? [] : module.errors ?? []
|
|
888
|
+
const declaration = declarations.find(({id}) => id == exported.declarationId)
|
|
889
|
+
|
|
890
|
+
if (!declaration) throw new Error(`Resolved export '${exported.declarationId}' has no declaration.`)
|
|
891
|
+
|
|
892
|
+
return declaration
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* Prefixes module-owned declaration identities and explicit resolved references to them.
|
|
897
|
+
* @param {import("../semantic/types.js").SemanticModule} module - Validated module.
|
|
898
|
+
* @param {string} moduleId - Stable module identity.
|
|
899
|
+
* @returns {void}
|
|
900
|
+
*/
|
|
901
|
+
function rekeyDeclarations(module, moduleId) {
|
|
902
|
+
const replacements = new Map()
|
|
903
|
+
|
|
904
|
+
for (const declaration of module.classes ?? []) {
|
|
905
|
+
replacements.set(declaration.id, `${moduleId}#${declaration.id}`)
|
|
906
|
+
replacements.set(declaration.constructor.id, `${moduleId}#${declaration.constructor.id}`)
|
|
907
|
+
for (const field of declaration.fields) replacements.set(field.id, `${moduleId}#${field.id}`)
|
|
908
|
+
for (const method of declaration.methods) replacements.set(method.id, `${moduleId}#${method.id}`)
|
|
909
|
+
}
|
|
910
|
+
for (const declaration of module.records ?? []) {
|
|
911
|
+
replacements.set(declaration.id, `${moduleId}#${declaration.id}`)
|
|
912
|
+
for (const parameter of declaration.typeParameters ?? []) {
|
|
913
|
+
if (parameter.id) replacements.set(parameter.id, `${moduleId}#${parameter.id}`)
|
|
914
|
+
}
|
|
915
|
+
for (const field of declaration.fields) replacements.set(field.id, `${moduleId}#${field.id}`)
|
|
916
|
+
}
|
|
917
|
+
for (const declaration of module.errors ?? []) replacements.set(declaration.id, `${moduleId}#${declaration.id}`)
|
|
918
|
+
for (const declaration of module.functions) {
|
|
919
|
+
replacements.set(declaration.id, `${moduleId}#${declaration.id}`)
|
|
920
|
+
for (const parameter of declaration.typeParameters ?? []) {
|
|
921
|
+
if (parameter.id) replacements.set(parameter.id, `${moduleId}#${parameter.id}`)
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
for (const declaration of module.records ?? []) {
|
|
926
|
+
declaration.id = replacements.get(declaration.id)
|
|
927
|
+
for (const parameter of declaration.typeParameters ?? []) {
|
|
928
|
+
parameter.id = /** @type {string} */ (replacements.get(/** @type {string} */ (parameter.id)))
|
|
929
|
+
}
|
|
930
|
+
for (const field of declaration.fields) field.id = /** @type {string} */ (replacements.get(field.id))
|
|
931
|
+
}
|
|
932
|
+
for (const declaration of module.errors ?? []) declaration.id = replacements.get(declaration.id)
|
|
933
|
+
for (const declaration of module.classes ?? []) {
|
|
934
|
+
declaration.id = replacements.get(declaration.id)
|
|
935
|
+
declaration.constructor.id = replacements.get(declaration.constructor.id)
|
|
936
|
+
for (const field of declaration.fields) field.id = replacements.get(field.id)
|
|
937
|
+
for (const method of declaration.methods) method.id = replacements.get(method.id)
|
|
938
|
+
if (declaration.ownership?.kind == "ownedResource") {
|
|
939
|
+
declaration.ownership.fieldId = /** @type {string} */ (replacements.get(declaration.ownership.fieldId))
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
for (const declaration of module.functions) {
|
|
943
|
+
declaration.id = replacements.get(declaration.id)
|
|
944
|
+
for (const parameter of declaration.typeParameters ?? []) {
|
|
945
|
+
parameter.id = /** @type {string} */ (replacements.get(/** @type {string} */ (parameter.id)))
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
const seen = new WeakSet()
|
|
950
|
+
/**
|
|
951
|
+
* Rewrites only typed identity-bearing fields throughout one semantic subtree.
|
|
952
|
+
* @param {unknown} value - Semantic subtree.
|
|
953
|
+
*/
|
|
954
|
+
function visit(value) {
|
|
955
|
+
if (!value || typeof value != "object" || seen.has(value)) return
|
|
956
|
+
seen.add(value)
|
|
957
|
+
const object = /** @type {Record<string, unknown>} */ (value)
|
|
958
|
+
|
|
959
|
+
if ((object.kind == "RecordType" || object.kind == "ReferenceType" || object.kind == "OwnedReferenceType" ||
|
|
960
|
+
object.kind == "ErrorType" || object.kind == "ResolvedFunctionSignature" ||
|
|
961
|
+
object.kind == "ResolvedConstructorSignature" || object.kind == "ResolvedMethodSignature") &&
|
|
962
|
+
typeof object.declarationId == "string" && replacements.has(object.declarationId)) {
|
|
963
|
+
object.declarationId = replacements.get(object.declarationId)
|
|
964
|
+
}
|
|
965
|
+
if ((object.kind == "MemberRead" || object.kind == "PrivateFieldRead" || object.kind == "PrivateFieldWriteStatement") &&
|
|
966
|
+
typeof object.field == "string" && replacements.has(object.field)) {
|
|
967
|
+
object.field = replacements.get(object.field)
|
|
968
|
+
}
|
|
969
|
+
if (object.kind == "MethodCallExpression" && typeof object.method == "string" && replacements.has(object.method)) {
|
|
970
|
+
object.method = replacements.get(object.method)
|
|
971
|
+
}
|
|
972
|
+
if (object.kind == "ReceiverExpression" && typeof object.classId == "string" && replacements.has(object.classId)) {
|
|
973
|
+
object.classId = replacements.get(object.classId)
|
|
974
|
+
}
|
|
975
|
+
if (object.kind == "TypeVariableReference" && typeof object.parameterId == "string" && replacements.has(object.parameterId)) {
|
|
976
|
+
object.parameterId = replacements.get(object.parameterId)
|
|
977
|
+
}
|
|
978
|
+
for (const child of Object.values(object)) if (typeof child == "object") visit(child)
|
|
979
|
+
}
|
|
980
|
+
visit(module)
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* Creates a source association for a parser-owned module header node.
|
|
985
|
+
* @param {import("../semantic/types.js").SourceLocation} location - Node origin.
|
|
986
|
+
* @param {Readonly<Record<string, import("../semantic/types.js").SourceLocation>>} ranges - Exact available header roles.
|
|
987
|
+
* @param {string} sourceId - Program-wide source identity.
|
|
988
|
+
* @returns {import("../semantic/types.js").SemanticNodeSourceProvenance} Source association.
|
|
989
|
+
*/
|
|
990
|
+
function sourceAssociation(location, ranges, sourceId) {
|
|
991
|
+
return {
|
|
992
|
+
origin: {kind: "source", location, sourceId},
|
|
993
|
+
ranges,
|
|
994
|
+
schema: "SemantifoldNodeProvenance",
|
|
995
|
+
version: 1
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
/**
|
|
1000
|
+
* Rebinds one parser-local provenance registry to the complete program source registry.
|
|
1001
|
+
* @param {import("../semantic/types.js").SemanticModule} module - Newly parsed module.
|
|
1002
|
+
* @param {import("../semantic/types.js").RegisteredSource[]} sources - Program-wide registry.
|
|
1003
|
+
* @param {string} sourceId - Owning source identity.
|
|
1004
|
+
* @returns {void}
|
|
1005
|
+
*/
|
|
1006
|
+
function rebaseParsedModuleProvenance(module, sources, sourceId) {
|
|
1007
|
+
if (!module.provenance) throw new Error("Parsed program module has no source provenance.")
|
|
1008
|
+
const seen = new WeakSet()
|
|
1009
|
+
|
|
1010
|
+
/**
|
|
1011
|
+
* Rebinds source-origin identities throughout one semantic subtree.
|
|
1012
|
+
* @param {unknown} value - Semantic subtree.
|
|
1013
|
+
*/
|
|
1014
|
+
function visit(value) {
|
|
1015
|
+
if (!value || typeof value != "object" || seen.has(value)) return
|
|
1016
|
+
seen.add(value)
|
|
1017
|
+
const candidate = /** @type {Record<string, unknown>} */ (value)
|
|
1018
|
+
const provenance = candidate.sourceProvenance
|
|
1019
|
+
const origin = isPlainObject(provenance) ? provenance.origin : undefined
|
|
1020
|
+
|
|
1021
|
+
if (isPlainObject(origin) && origin.kind == "source") origin.sourceId = sourceId
|
|
1022
|
+
for (const child of Object.values(candidate)) visit(child)
|
|
1023
|
+
}
|
|
1024
|
+
visit(module)
|
|
1025
|
+
for (const node of module.provenance.nodes) {
|
|
1026
|
+
if (node.origin.kind == "source") node.origin.sourceId = sourceId
|
|
1027
|
+
}
|
|
1028
|
+
module.provenance.sources = sources
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/**
|
|
1032
|
+
* Reports whether a candidate is a direct plain object.
|
|
1033
|
+
* @param {unknown} value - Candidate object.
|
|
1034
|
+
* @returns {value is Record<string, unknown>} Whether the value is a plain object.
|
|
1035
|
+
*/
|
|
1036
|
+
function isPlainObject(value) {
|
|
1037
|
+
return Boolean(value) && typeof value == "object" && !Array.isArray(value) && Object.getPrototypeOf(value) == Object.prototype
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
/**
|
|
1041
|
+
* Reads a value whose map key was validated earlier in program assembly.
|
|
1042
|
+
* @template T
|
|
1043
|
+
* @param {Map<string, T>} map - Map with a previously validated key.
|
|
1044
|
+
* @param {string} key - Required key.
|
|
1045
|
+
* @returns {T} Stored value.
|
|
1046
|
+
*/
|
|
1047
|
+
function requiredMapValue(map, key) {
|
|
1048
|
+
const value = map.get(key)
|
|
1049
|
+
|
|
1050
|
+
if (value === undefined) throw new Error(`Missing validated map value '${key}'.`)
|
|
1051
|
+
|
|
1052
|
+
return value
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
/**
|
|
1056
|
+
* Raises a stable malformed program request diagnostic.
|
|
1057
|
+
* @param {string} message - Stable request failure detail.
|
|
1058
|
+
* @returns {never} Always throws.
|
|
1059
|
+
*/
|
|
1060
|
+
function invalidProgram(message) {
|
|
1061
|
+
throw new SemantifoldDiagnostic({code: "INVALID_PROGRAM", language: "program", message})
|
|
1062
|
+
}
|