semantifold 0.1.0 → 0.2.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 +13 -6
- package/build/index.d.ts +32 -3
- package/build/index.d.ts.map +1 -1
- package/build/index.js +99 -4
- package/build/src/acceptance.d.ts +22 -0
- package/build/src/acceptance.d.ts.map +1 -0
- package/build/src/acceptance.js +339 -0
- package/build/src/array.d.ts +7 -0
- package/build/src/array.d.ts.map +1 -0
- package/build/src/array.js +18 -0
- package/build/src/artifact-path.d.ts +13 -0
- package/build/src/artifact-path.d.ts.map +1 -0
- package/build/src/artifact-path.js +30 -0
- package/build/src/artifacts.d.ts +7 -0
- package/build/src/artifacts.d.ts.map +1 -0
- package/build/src/artifacts.js +389 -0
- package/build/src/backends/identifiers.d.ts.map +1 -1
- package/build/src/backends/identifiers.js +10 -2
- package/build/src/backends/index.d.ts.map +1 -1
- package/build/src/backends/index.js +19 -37
- package/build/src/backends/python.d.ts +8 -0
- package/build/src/backends/python.d.ts.map +1 -0
- package/build/src/backends/python.js +162 -0
- package/build/src/backends/scalars.d.ts.map +1 -1
- package/build/src/backends/scalars.js +2 -1
- package/build/src/backends/shared.d.ts.map +1 -1
- package/build/src/backends/shared.js +9 -6
- package/build/src/binary-mapping.d.ts +32 -0
- package/build/src/binary-mapping.d.ts.map +1 -0
- package/build/src/binary-mapping.js +285 -0
- package/build/src/diagnostic.d.ts +36 -4
- package/build/src/diagnostic.d.ts.map +1 -1
- package/build/src/diagnostic.js +42 -3
- package/build/src/frontends/index.d.ts.map +1 -1
- package/build/src/frontends/index.js +5 -23
- package/build/src/frontends/python.d.ts +14 -0
- package/build/src/frontends/python.d.ts.map +1 -0
- package/build/src/frontends/python.js +615 -0
- package/build/src/frontends/scalars.d.ts.map +1 -1
- package/build/src/frontends/scalars.js +6 -1
- package/build/src/language-registry.d.ts +113 -0
- package/build/src/language-registry.d.ts.map +1 -0
- package/build/src/language-registry.js +372 -0
- package/build/src/mapping.js +3 -2
- package/build/src/semantic/location.d.ts +7 -0
- package/build/src/semantic/location.d.ts.map +1 -1
- package/build/src/semantic/location.js +10 -1
- package/build/src/semantic/provenance.js +3 -3
- package/build/src/semantic/types.d.ts +399 -2
- package/build/src/semantic/types.d.ts.map +1 -1
- package/build/src/semantic/types.js +110 -2
- package/build/src/subprocess.d.ts +29 -0
- package/build/src/subprocess.d.ts.map +1 -0
- package/build/src/subprocess.js +207 -0
- package/build/src/timeout.d.ts +8 -0
- package/build/src/timeout.d.ts.map +1 -0
- package/build/src/timeout.js +11 -0
- package/build/src/toolchains.d.ts +83 -0
- package/build/src/toolchains.d.ts.map +1 -0
- package/build/src/toolchains.js +413 -0
- package/docs/architecture.md +7 -3
- package/docs/language-support.md +25 -4
- package/docs/parser-qualification.md +70 -0
- package/docs/source-maps.md +31 -4
- package/docs/testing.md +21 -2
- package/index.js +109 -4
- package/package.json +4 -2
- package/src/acceptance.js +359 -0
- package/src/array.js +19 -0
- package/src/artifact-path.js +34 -0
- package/src/artifacts.js +420 -0
- package/src/backends/identifiers.js +9 -1
- package/src/backends/index.js +16 -32
- package/src/backends/python.js +175 -0
- package/src/backends/scalars.js +1 -0
- package/src/backends/shared.js +8 -5
- package/src/binary-mapping.js +305 -0
- package/src/diagnostic.js +34 -2
- package/src/frontends/index.js +5 -20
- package/src/frontends/python.js +689 -0
- package/src/frontends/scalars.js +5 -0
- package/src/language-registry.js +403 -0
- package/src/mapping.js +2 -1
- package/src/semantic/location.js +10 -0
- package/src/semantic/provenance.js +2 -2
- package/src/semantic/types.js +124 -1
- package/src/subprocess.js +203 -0
- package/src/timeout.js +12 -0
- package/src/toolchains.js +444 -0
- package/tsconfig.json +1 -0
package/src/frontends/scalars.js
CHANGED
|
@@ -31,6 +31,11 @@ const sourceScalarTypes = Object.freeze({
|
|
|
31
31
|
["bool", "boolean"],
|
|
32
32
|
["string", "string"]
|
|
33
33
|
]),
|
|
34
|
+
python: sourceTypeMap([
|
|
35
|
+
["int", "integer"],
|
|
36
|
+
["bool", "boolean"],
|
|
37
|
+
["str", "string"]
|
|
38
|
+
]),
|
|
34
39
|
ruby: sourceTypeMap([
|
|
35
40
|
["[Integer]", "integer"],
|
|
36
41
|
["[bool]", "boolean"],
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import {isDenseArray} from "./array.js"
|
|
4
|
+
import {SemantifoldDiagnostic, unsupportedRole} from "./diagnostic.js"
|
|
5
|
+
import {generateJava} from "./backends/java.js"
|
|
6
|
+
import {generateJavaScript} from "./backends/javascript.js"
|
|
7
|
+
import {generatePhp} from "./backends/php.js"
|
|
8
|
+
import {generatePython} from "./backends/python.js"
|
|
9
|
+
import {generateRuby} from "./backends/ruby.js"
|
|
10
|
+
import {generateTypeScript} from "./backends/typescript.js"
|
|
11
|
+
import {parseJava} from "./frontends/java.js"
|
|
12
|
+
import {parseJavaScriptTypeScript} from "./frontends/javascript-typescript.js"
|
|
13
|
+
import {parsePhp} from "./frontends/php.js"
|
|
14
|
+
import {parsePython} from "./frontends/python.js"
|
|
15
|
+
import {parseRuby} from "./frontends/ruby.js"
|
|
16
|
+
|
|
17
|
+
const registryRoles = Object.freeze(["frontend", "textBackend", "binaryBackend", "applicationBackend", "interoperability"])
|
|
18
|
+
const acceptanceStageOrder = ["parse", "generate", "compile", "link", "validate", "instantiate", "execute"]
|
|
19
|
+
const acceptanceStages = new Map(acceptanceStageOrder.map((stage, index) => [stage, index]))
|
|
20
|
+
const registryKeys = new Set([
|
|
21
|
+
"acceptance", "applicationBackend", "artifactMultiplicity", "binaryBackend", "defaultFilename", "frontend", "id",
|
|
22
|
+
"interoperability", "mapping", "mediaType", "roundTrip", "textBackend"
|
|
23
|
+
])
|
|
24
|
+
|
|
25
|
+
/** @typedef {"frontend" | "textBackend" | "binaryBackend" | "applicationBackend" | "interoperability"} RegistryRole */
|
|
26
|
+
/** @typedef {(input: {filename: string, source: string}) => import("./semantic/types.js").SemanticModule} Frontend */
|
|
27
|
+
/** @typedef {(...values: unknown[]) => unknown} RegistryImplementation */
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @typedef LanguageRegistryRecord
|
|
31
|
+
* @property {string} id - Stable identity.
|
|
32
|
+
* @property {{stages: import("./semantic/types.js").AcceptanceStage[], toolchains: string[]}} acceptance - Acceptance declaration.
|
|
33
|
+
* @property {"single" | "multiple"} artifactMultiplicity - Artifact multiplicity.
|
|
34
|
+
* @property {import("./semantic/types.js").LanguageMappingCapabilities} mapping - Mapping capabilities.
|
|
35
|
+
* @property {boolean} roundTrip - Round-trip declaration.
|
|
36
|
+
* @property {string} [defaultFilename] - Default backend filename.
|
|
37
|
+
* @property {string} [mediaType] - Default backend media type.
|
|
38
|
+
* @property {RegistryImplementation} [frontend] - Frontend adapter.
|
|
39
|
+
* @property {RegistryImplementation} [textBackend] - Text backend.
|
|
40
|
+
* @property {RegistryImplementation} [binaryBackend] - Binary backend.
|
|
41
|
+
* @property {RegistryImplementation} [applicationBackend] - Application-artifact backend.
|
|
42
|
+
* @property {RegistryImplementation} [interoperability] - Interoperability bridge.
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @typedef LanguageRegistry
|
|
47
|
+
* @property {readonly import("./semantic/types.js").LanguageCapabilities[]} descriptors - Public descriptors.
|
|
48
|
+
* @property {readonly string[]} ids - Registered IDs in stable order.
|
|
49
|
+
* @property {(id: string) => Readonly<LanguageRegistryRecord>} record - Exact record lookup.
|
|
50
|
+
* @property {(id: string, role: RegistryRole, location?: import("./semantic/types.js").SourceLocation) => RegistryImplementation} resolve - Role lookup.
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Constructs an immutable role registry from ordered records.
|
|
55
|
+
* @param {unknown} candidateRecords - Ordered language records.
|
|
56
|
+
* @returns {Readonly<LanguageRegistry>} Registry.
|
|
57
|
+
*/
|
|
58
|
+
export function createLanguageRegistry(candidateRecords) {
|
|
59
|
+
if (!isDenseArray(candidateRecords)) invalidRegistry("Registry records must be an ordered dense array.")
|
|
60
|
+
|
|
61
|
+
/** @type {Map<string, Readonly<LanguageRegistryRecord>>} */
|
|
62
|
+
const recordsById = new Map()
|
|
63
|
+
/** @type {import("./semantic/types.js").LanguageCapabilities[]} */
|
|
64
|
+
const descriptors = []
|
|
65
|
+
|
|
66
|
+
for (let recordIndex = 0; recordIndex < candidateRecords.length; recordIndex += 1) {
|
|
67
|
+
const candidate = candidateRecords[recordIndex]
|
|
68
|
+
|
|
69
|
+
if (!isPlainObject(candidate)) invalidRegistry("Every registry record must be a plain object.")
|
|
70
|
+
const id = candidate.id
|
|
71
|
+
|
|
72
|
+
if (typeof id != "string" || id.length == 0 || !/^[a-z][a-z0-9-]*$/u.test(id)) {
|
|
73
|
+
invalidRegistry("Every registry record requires a stable lowercase ID.", typeof id == "string" ? id : undefined)
|
|
74
|
+
}
|
|
75
|
+
if (recordsById.has(id)) invalidRegistry(`Duplicate registry ID '${id}'.`, id)
|
|
76
|
+
if (Object.keys(candidate).some((key) => !registryKeys.has(key))) invalidRegistry(`Registry record '${id}' has unknown fields.`, id)
|
|
77
|
+
|
|
78
|
+
for (const role of registryRoles) {
|
|
79
|
+
if (candidate[role] !== undefined && typeof candidate[role] != "function") {
|
|
80
|
+
invalidRegistry(`Registry role '${role}' for '${id}' must be a function when present.`, id)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const artifactMultiplicity = candidate.artifactMultiplicity
|
|
84
|
+
|
|
85
|
+
if (artifactMultiplicity != "single" && artifactMultiplicity != "multiple") {
|
|
86
|
+
invalidRegistry(`Registry record '${id}' has an invalid artifact multiplicity.`, id)
|
|
87
|
+
}
|
|
88
|
+
if (typeof candidate.roundTrip != "boolean") invalidRegistry(`Registry record '${id}' requires a Boolean round-trip declaration.`, id)
|
|
89
|
+
const mappingCandidate = candidate.mapping
|
|
90
|
+
|
|
91
|
+
if (!isPlainObject(mappingCandidate)) invalidRegistry(`Registry record '${id}' has an invalid mapping declaration.`, id)
|
|
92
|
+
const mappingSnapshot = {
|
|
93
|
+
binaryRanges: mappingCandidate.binaryRanges,
|
|
94
|
+
richText: mappingCandidate.richText,
|
|
95
|
+
sourceMapV3: mappingCandidate.sourceMapV3
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (typeof mappingSnapshot.richText != "boolean" || typeof mappingSnapshot.sourceMapV3 != "boolean" ||
|
|
99
|
+
typeof mappingSnapshot.binaryRanges != "boolean") {
|
|
100
|
+
invalidRegistry(`Registry record '${id}' has an invalid mapping declaration.`, id)
|
|
101
|
+
}
|
|
102
|
+
const mapping = deepFreeze(/** @type {import("./semantic/types.js").LanguageMappingCapabilities} */ (mappingSnapshot))
|
|
103
|
+
const acceptanceCandidate = candidate.acceptance
|
|
104
|
+
|
|
105
|
+
if (!isPlainObject(acceptanceCandidate) || !isDenseArray(acceptanceCandidate.stages) ||
|
|
106
|
+
!isDenseArray(acceptanceCandidate.toolchains)) {
|
|
107
|
+
invalidRegistry(`Registry record '${id}' has an invalid acceptance declaration.`, id)
|
|
108
|
+
}
|
|
109
|
+
/** @type {unknown[]} */
|
|
110
|
+
const stageSnapshot = []
|
|
111
|
+
/** @type {unknown[]} */
|
|
112
|
+
const toolchainSnapshot = []
|
|
113
|
+
|
|
114
|
+
for (let index = 0; index < acceptanceCandidate.stages.length; index += 1) {
|
|
115
|
+
stageSnapshot.push(acceptanceCandidate.stages[index])
|
|
116
|
+
}
|
|
117
|
+
for (let index = 0; index < acceptanceCandidate.toolchains.length; index += 1) {
|
|
118
|
+
toolchainSnapshot.push(acceptanceCandidate.toolchains[index])
|
|
119
|
+
}
|
|
120
|
+
if (!stageSnapshot.every((stage) => typeof stage == "string" && acceptanceStages.has(stage)) ||
|
|
121
|
+
!toolchainSnapshot.every((toolchain) => typeof toolchain == "string" && toolchain.length > 0)) {
|
|
122
|
+
invalidRegistry(`Registry record '${id}' has an invalid acceptance declaration.`, id)
|
|
123
|
+
}
|
|
124
|
+
const stages = /** @type {import("./semantic/types.js").AcceptanceStage[]} */ (stageSnapshot)
|
|
125
|
+
const toolchains = /** @type {string[]} */ (toolchainSnapshot)
|
|
126
|
+
let previousStage = -1
|
|
127
|
+
const declaredStages = new Set()
|
|
128
|
+
|
|
129
|
+
for (const stage of stages) {
|
|
130
|
+
const stageIndex = /** @type {number} */ (acceptanceStages.get(/** @type {string} */ (stage)))
|
|
131
|
+
|
|
132
|
+
if (declaredStages.has(stage) || stageIndex < previousStage) invalidRegistry(`Registry record '${id}' has unordered or duplicate acceptance stages.`, id)
|
|
133
|
+
declaredStages.add(stage)
|
|
134
|
+
previousStage = stageIndex
|
|
135
|
+
}
|
|
136
|
+
if (new Set(toolchains).size != toolchains.length) {
|
|
137
|
+
invalidRegistry(`Registry record '${id}' has duplicate acceptance toolchains.`, id)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const hasFrontend = typeof candidate.frontend == "function"
|
|
141
|
+
const hasTextBackend = typeof candidate.textBackend == "function"
|
|
142
|
+
const hasBinaryBackend = typeof candidate.binaryBackend == "function"
|
|
143
|
+
const hasApplicationBackend = typeof candidate.applicationBackend == "function"
|
|
144
|
+
const hasBackend = hasTextBackend || hasBinaryBackend || hasApplicationBackend
|
|
145
|
+
|
|
146
|
+
if ((mapping.richText || mapping.sourceMapV3) && !hasTextBackend ||
|
|
147
|
+
mapping.sourceMapV3 && !mapping.richText ||
|
|
148
|
+
mapping.binaryRanges && !hasBinaryBackend && !hasApplicationBackend) {
|
|
149
|
+
invalidRegistry(`Registry record '${id}' declares mapping support without its required backend role.`, id)
|
|
150
|
+
}
|
|
151
|
+
if (candidate.roundTrip && (!hasFrontend || !hasBackend)) {
|
|
152
|
+
invalidRegistry(`Registry record '${id}' declares round-trip support without frontend and backend roles.`, id)
|
|
153
|
+
}
|
|
154
|
+
if (stages.includes("parse") && !hasFrontend || stages.includes("generate") && !hasBackend) {
|
|
155
|
+
invalidRegistry(`Registry record '${id}' declares an acceptance stage without its required role.`, id)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if ((hasBackend || candidate.defaultFilename !== undefined) &&
|
|
159
|
+
(typeof candidate.defaultFilename != "string" || candidate.defaultFilename.length == 0)) {
|
|
160
|
+
invalidRegistry(`Registry backend '${id}' requires a default filename.`, id)
|
|
161
|
+
}
|
|
162
|
+
if ((hasBackend || candidate.mediaType !== undefined) &&
|
|
163
|
+
(typeof candidate.mediaType != "string" || candidate.mediaType.length == 0)) {
|
|
164
|
+
invalidRegistry(`Registry backend '${id}' requires a media type.`, id)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const acceptance = deepFreeze(/** @type {import("./semantic/types.js").LanguageAcceptanceCapabilities} */ ({
|
|
168
|
+
stages,
|
|
169
|
+
toolchains
|
|
170
|
+
}))
|
|
171
|
+
const record = deepFreeze(/** @type {LanguageRegistryRecord} */ ({
|
|
172
|
+
acceptance,
|
|
173
|
+
artifactMultiplicity,
|
|
174
|
+
id,
|
|
175
|
+
mapping,
|
|
176
|
+
roundTrip: candidate.roundTrip,
|
|
177
|
+
...(typeof candidate.defaultFilename == "string" ? {defaultFilename: candidate.defaultFilename} : {}),
|
|
178
|
+
...(typeof candidate.mediaType == "string" ? {mediaType: candidate.mediaType} : {}),
|
|
179
|
+
...(typeof candidate.frontend == "function" ? {frontend: candidate.frontend} : {}),
|
|
180
|
+
...(typeof candidate.textBackend == "function" ? {textBackend: candidate.textBackend} : {}),
|
|
181
|
+
...(typeof candidate.binaryBackend == "function" ? {binaryBackend: candidate.binaryBackend} : {}),
|
|
182
|
+
...(typeof candidate.applicationBackend == "function" ? {applicationBackend: candidate.applicationBackend} : {}),
|
|
183
|
+
...(typeof candidate.interoperability == "function" ? {interoperability: candidate.interoperability} : {})
|
|
184
|
+
}))
|
|
185
|
+
const descriptor = deepFreeze(/** @type {import("./semantic/types.js").LanguageCapabilities} */ ({
|
|
186
|
+
acceptance,
|
|
187
|
+
artifactMultiplicity,
|
|
188
|
+
id,
|
|
189
|
+
mapping,
|
|
190
|
+
roles: {
|
|
191
|
+
applicationBackend: typeof candidate.applicationBackend == "function",
|
|
192
|
+
binaryBackend: typeof candidate.binaryBackend == "function",
|
|
193
|
+
frontend: typeof candidate.frontend == "function",
|
|
194
|
+
interoperability: typeof candidate.interoperability == "function",
|
|
195
|
+
textBackend: typeof candidate.textBackend == "function"
|
|
196
|
+
},
|
|
197
|
+
roundTrip: candidate.roundTrip
|
|
198
|
+
}))
|
|
199
|
+
|
|
200
|
+
recordsById.set(id, record)
|
|
201
|
+
descriptors.push(descriptor)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const frozenDescriptors = deepFreeze(descriptors)
|
|
205
|
+
const ids = Object.freeze(frozenDescriptors.map(({id}) => id))
|
|
206
|
+
|
|
207
|
+
return Object.freeze({
|
|
208
|
+
descriptors: frozenDescriptors,
|
|
209
|
+
ids,
|
|
210
|
+
record(id) {
|
|
211
|
+
const record = recordsById.get(id)
|
|
212
|
+
|
|
213
|
+
if (!record) unsupportedLanguage(id)
|
|
214
|
+
|
|
215
|
+
return record
|
|
216
|
+
},
|
|
217
|
+
resolve(id, role, location) {
|
|
218
|
+
if (!registryRoles.includes(role)) invalidRegistry(`Unknown registry role '${role}'.`, id)
|
|
219
|
+
const record = recordsById.get(id)
|
|
220
|
+
|
|
221
|
+
if (!record) unsupportedLanguage(id, location, role)
|
|
222
|
+
const implementation = record[role]
|
|
223
|
+
|
|
224
|
+
if (typeof implementation != "function") unsupportedRole(id, publicRoleName(role), location)
|
|
225
|
+
|
|
226
|
+
return implementation
|
|
227
|
+
}
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* PHP registry frontend wrapper.
|
|
233
|
+
* @type {Frontend}
|
|
234
|
+
*/
|
|
235
|
+
const phpFrontend = ({filename, source}) => parsePhp({filename, source})
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Ruby registry frontend wrapper.
|
|
239
|
+
* @type {Frontend}
|
|
240
|
+
*/
|
|
241
|
+
const rubyFrontend = ({filename, source}) => parseRuby({filename, source})
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* JavaScript registry frontend wrapper.
|
|
245
|
+
* @type {Frontend}
|
|
246
|
+
*/
|
|
247
|
+
const javaScriptFrontend = ({filename, source}) => parseJavaScriptTypeScript({filename, language: "javascript", source})
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* TypeScript registry frontend wrapper.
|
|
251
|
+
* @type {Frontend}
|
|
252
|
+
*/
|
|
253
|
+
const typeScriptFrontend = ({filename, source}) => parseJavaScriptTypeScript({filename, language: "typescript", source})
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Java registry frontend wrapper.
|
|
257
|
+
* @type {Frontend}
|
|
258
|
+
*/
|
|
259
|
+
const javaFrontend = ({filename, source}) => parseJava({filename, source})
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Python registry frontend wrapper.
|
|
263
|
+
* @type {Frontend}
|
|
264
|
+
*/
|
|
265
|
+
const pythonFrontend = ({filename, source}) => parsePython({filename, source})
|
|
266
|
+
|
|
267
|
+
const records = [
|
|
268
|
+
language({
|
|
269
|
+
acceptance: {stages: ["parse", "generate", "execute"], toolchains: ["php"]},
|
|
270
|
+
defaultFilename: "program.php",
|
|
271
|
+
frontend: phpFrontend,
|
|
272
|
+
id: "php",
|
|
273
|
+
mediaType: "application/x-httpd-php",
|
|
274
|
+
textBackend: generatePhp
|
|
275
|
+
}),
|
|
276
|
+
language({
|
|
277
|
+
acceptance: {stages: ["parse", "generate", "execute"], toolchains: ["ruby"]},
|
|
278
|
+
defaultFilename: "program.rb",
|
|
279
|
+
frontend: rubyFrontend,
|
|
280
|
+
id: "ruby",
|
|
281
|
+
mediaType: "text/x-ruby",
|
|
282
|
+
textBackend: generateRuby
|
|
283
|
+
}),
|
|
284
|
+
language({
|
|
285
|
+
acceptance: {stages: ["parse", "generate", "execute"], toolchains: ["node"]},
|
|
286
|
+
defaultFilename: "program.js",
|
|
287
|
+
frontend: javaScriptFrontend,
|
|
288
|
+
id: "javascript",
|
|
289
|
+
mediaType: "text/javascript",
|
|
290
|
+
textBackend: generateJavaScript
|
|
291
|
+
}),
|
|
292
|
+
language({
|
|
293
|
+
acceptance: {stages: ["parse", "generate", "compile", "execute"], toolchains: ["tsc", "node"]},
|
|
294
|
+
defaultFilename: "program.ts",
|
|
295
|
+
frontend: typeScriptFrontend,
|
|
296
|
+
id: "typescript",
|
|
297
|
+
mediaType: "text/typescript",
|
|
298
|
+
textBackend: generateTypeScript
|
|
299
|
+
}),
|
|
300
|
+
language({
|
|
301
|
+
acceptance: {stages: ["parse", "generate", "compile", "execute"], toolchains: ["javac", "java"]},
|
|
302
|
+
defaultFilename: "Main.java",
|
|
303
|
+
frontend: javaFrontend,
|
|
304
|
+
id: "java",
|
|
305
|
+
mediaType: "text/x-java-source",
|
|
306
|
+
textBackend: generateJava
|
|
307
|
+
}),
|
|
308
|
+
language({
|
|
309
|
+
acceptance: {stages: ["parse", "generate", "compile", "execute"], toolchains: ["python"]},
|
|
310
|
+
defaultFilename: "program.py",
|
|
311
|
+
frontend: pythonFrontend,
|
|
312
|
+
id: "python",
|
|
313
|
+
mediaType: "text/x-python",
|
|
314
|
+
textBackend: generatePython
|
|
315
|
+
})
|
|
316
|
+
]
|
|
317
|
+
|
|
318
|
+
export const languageRegistry = createLanguageRegistry(records)
|
|
319
|
+
export const languageCapabilities = languageRegistry.descriptors
|
|
320
|
+
/** @type {readonly import("./semantic/types.js").SemanticLanguage[]} */
|
|
321
|
+
export const supportedLanguages = Object.freeze(languageCapabilities
|
|
322
|
+
.filter(({roles}) => roles.frontend && roles.textBackend)
|
|
323
|
+
.map(({id}) => /** @type {import("./semantic/types.js").SemanticLanguage} */ (id)))
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Supplies common original-five capability declarations.
|
|
327
|
+
* @param {Record<string, unknown>} values - Language-specific record fields.
|
|
328
|
+
* @returns {Record<string, unknown>} Complete record.
|
|
329
|
+
*/
|
|
330
|
+
function language(values) {
|
|
331
|
+
return {
|
|
332
|
+
artifactMultiplicity: "single",
|
|
333
|
+
mapping: {binaryRanges: false, richText: true, sourceMapV3: true},
|
|
334
|
+
roundTrip: true,
|
|
335
|
+
...values
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Formats an internal registry key for a public role diagnostic.
|
|
341
|
+
* @param {string} role - Internal role key.
|
|
342
|
+
* @returns {string} Public role name.
|
|
343
|
+
*/
|
|
344
|
+
function publicRoleName(role) {
|
|
345
|
+
if (role == "textBackend") return "text backend"
|
|
346
|
+
if (role == "binaryBackend") return "binary backend"
|
|
347
|
+
if (role == "applicationBackend") return "application-artifact backend"
|
|
348
|
+
|
|
349
|
+
return role
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Throws an unknown-language diagnostic.
|
|
354
|
+
* @param {string} id - Unknown identity.
|
|
355
|
+
* @param {import("./semantic/types.js").SourceLocation | undefined} [location] - Related location.
|
|
356
|
+
* @param {RegistryRole | undefined} [role] - Requested role.
|
|
357
|
+
* @returns {never} Always throws.
|
|
358
|
+
*/
|
|
359
|
+
function unsupportedLanguage(id, location, role) {
|
|
360
|
+
const message = role == "frontend" ? "No frontend adapter is registered for this language." :
|
|
361
|
+
role == "textBackend" ? "No source backend is registered for this language." :
|
|
362
|
+
"No language or target is registered for this ID."
|
|
363
|
+
|
|
364
|
+
throw new SemantifoldDiagnostic({
|
|
365
|
+
code: "UNSUPPORTED_LANGUAGE",
|
|
366
|
+
language: id,
|
|
367
|
+
location,
|
|
368
|
+
message
|
|
369
|
+
})
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Throws a normalized registry-shape diagnostic.
|
|
374
|
+
* @param {string} message - Failure detail.
|
|
375
|
+
* @param {string | undefined} [id] - Candidate registry ID.
|
|
376
|
+
* @returns {never} Always throws.
|
|
377
|
+
*/
|
|
378
|
+
function invalidRegistry(message, id = "registry") {
|
|
379
|
+
throw new SemantifoldDiagnostic({code: "INVALID_REGISTRY", language: id, message})
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Checks for an ordinary string-keyed object.
|
|
384
|
+
* @param {unknown} value - Candidate value.
|
|
385
|
+
* @returns {value is Record<string, unknown>} Whether the value is a plain object.
|
|
386
|
+
*/
|
|
387
|
+
function isPlainObject(value) {
|
|
388
|
+
return typeof value == "object" && value != null && !Array.isArray(value) && Object.getPrototypeOf(value) == Object.prototype
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Deeply freezes registry records and public descriptors.
|
|
393
|
+
* @template T
|
|
394
|
+
* @param {T} value - Registry-owned value.
|
|
395
|
+
* @returns {T} Frozen value.
|
|
396
|
+
*/
|
|
397
|
+
function deepFreeze(value) {
|
|
398
|
+
if (!value || typeof value != "object" || Object.isFrozen(value)) return value
|
|
399
|
+
|
|
400
|
+
for (const nested of Object.values(value)) deepFreeze(nested)
|
|
401
|
+
|
|
402
|
+
return Object.freeze(value)
|
|
403
|
+
}
|
package/src/mapping.js
CHANGED
|
@@ -1104,7 +1104,8 @@ function validPoint(value) {
|
|
|
1104
1104
|
* @returns {value is import("./semantic/types.js").SemanticLanguage} Whether supported.
|
|
1105
1105
|
*/
|
|
1106
1106
|
function isLanguage(value) {
|
|
1107
|
-
return value == "php" || value == "ruby" || value == "javascript" || value == "typescript" || value == "java"
|
|
1107
|
+
return value == "php" || value == "ruby" || value == "javascript" || value == "typescript" || value == "java" ||
|
|
1108
|
+
value == "python"
|
|
1108
1109
|
}
|
|
1109
1110
|
|
|
1110
1111
|
/**
|
package/src/semantic/location.js
CHANGED
|
@@ -143,6 +143,16 @@ export function locationFromOffsets(filename, source, startOffset, endOffset) {
|
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Checks the one-based lexicographic order of a source range's endpoints.
|
|
148
|
+
* @param {import("./types.js").SourcePoint} start - Inclusive range start.
|
|
149
|
+
* @param {import("./types.js").SourcePoint} end - Exclusive range end.
|
|
150
|
+
* @returns {boolean} Whether end is at or after start.
|
|
151
|
+
*/
|
|
152
|
+
export function isSourceRangeOrdered(start, end) {
|
|
153
|
+
return end.line > start.line || end.line == start.line && end.column >= start.column
|
|
154
|
+
}
|
|
155
|
+
|
|
146
156
|
/**
|
|
147
157
|
* Builds a location spanning a complete source file.
|
|
148
158
|
* @param {string} filename - Originating filename.
|
|
@@ -185,7 +185,7 @@ export function createGenerationIndex(module, providedSources = []) {
|
|
|
185
185
|
*/
|
|
186
186
|
function addSource(filename, content, language) {
|
|
187
187
|
if (typeof filename != "string" || content !== null && typeof content != "string" ||
|
|
188
|
-
language !== null && !["php", "ruby", "javascript", "typescript", "java"].includes(language)) {
|
|
188
|
+
language !== null && !["php", "ruby", "javascript", "typescript", "java", "python"].includes(language)) {
|
|
189
189
|
throw new TypeError("Malformed semantic source registry entry.")
|
|
190
190
|
}
|
|
191
191
|
|
|
@@ -242,7 +242,7 @@ function usableSourceProvenance(provenance) {
|
|
|
242
242
|
if (!source || typeof source.id != "string" || sourceIds.has(source.id) || typeof source.filename != "string" ||
|
|
243
243
|
source.filename.length == 0 ||
|
|
244
244
|
source.content !== null && typeof source.content != "string" || source.language !== null &&
|
|
245
|
-
!["php", "ruby", "javascript", "typescript", "java"].includes(source.language)) return undefined
|
|
245
|
+
!["php", "ruby", "javascript", "typescript", "java", "python"].includes(source.language)) return undefined
|
|
246
246
|
sourceIds.add(source.id)
|
|
247
247
|
}
|
|
248
248
|
|
package/src/semantic/types.js
CHANGED
|
@@ -1,11 +1,45 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
-
/** @typedef {"php" | "ruby" | "javascript" | "typescript" | "java"} SemanticLanguage */
|
|
3
|
+
/** @typedef {"php" | "ruby" | "javascript" | "typescript" | "java" | "python"} SemanticLanguage */
|
|
4
4
|
/** @typedef {"integer" | "boolean" | "string"} SemanticTypeName */
|
|
5
5
|
/** @typedef {"IntegerNegate" | "BooleanNot"} SemanticUnaryOperation */
|
|
6
6
|
/** @typedef {"IntegerAdd" | "IntegerSubtract" | "IntegerMultiply" | "BooleanAnd" | "BooleanOr" | "IntegerEqual" | "IntegerNotEqual" | "BooleanEqual" | "BooleanNotEqual" | "StringEqual" | "StringNotEqual" | "IntegerLessThan" | "IntegerLessThanOrEqual" | "IntegerGreaterThan" | "IntegerGreaterThanOrEqual" | "StringConcat"} SemanticBinaryOperation */
|
|
7
7
|
/** @typedef {"function" | "parameter" | "local"} SemanticSymbolKind */
|
|
8
8
|
/** @typedef {"declaration" | "read" | "write" | "call"} SemanticSymbolRole */
|
|
9
|
+
/** @typedef {"parse" | "generate" | "compile" | "link" | "validate" | "instantiate" | "execute"} AcceptanceStage */
|
|
10
|
+
/** @typedef {"entry" | "source" | "manifest" | "support" | "mapping" | "resource" | "loader"} GeneratedArtifactRole */
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @typedef LanguageRoleCapabilities
|
|
14
|
+
* @property {boolean} frontend - Parser adapter availability.
|
|
15
|
+
* @property {boolean} textBackend - Single- or multi-file text backend availability.
|
|
16
|
+
* @property {boolean} binaryBackend - Binary backend availability.
|
|
17
|
+
* @property {boolean} applicationBackend - Application-artifact backend availability.
|
|
18
|
+
* @property {boolean} interoperability - Interoperability bridge availability.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef LanguageMappingCapabilities
|
|
23
|
+
* @property {boolean} richText - Authoritative rich text mappings.
|
|
24
|
+
* @property {boolean} sourceMapV3 - Source Map v3 projection.
|
|
25
|
+
* @property {boolean} binaryRanges - Byte-coordinate binary/resource mappings.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @typedef LanguageAcceptanceCapabilities
|
|
30
|
+
* @property {readonly AcceptanceStage[]} stages - Applicable ordered acceptance stages.
|
|
31
|
+
* @property {readonly string[]} toolchains - Required canonical toolchain IDs.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @typedef LanguageCapabilities
|
|
36
|
+
* @property {string} id - Stable registry identity.
|
|
37
|
+
* @property {Readonly<LanguageRoleCapabilities>} roles - Independently registered roles.
|
|
38
|
+
* @property {"single" | "multiple"} artifactMultiplicity - Target artifact multiplicity.
|
|
39
|
+
* @property {boolean} roundTrip - Whether frontend/backend round-trip acceptance is declared.
|
|
40
|
+
* @property {Readonly<LanguageMappingCapabilities>} mapping - Mapping forms.
|
|
41
|
+
* @property {Readonly<LanguageAcceptanceCapabilities>} acceptance - Stages and toolchains.
|
|
42
|
+
*/
|
|
9
43
|
|
|
10
44
|
/**
|
|
11
45
|
* @typedef SourcePoint
|
|
@@ -60,6 +94,24 @@
|
|
|
60
94
|
|
|
61
95
|
/** @typedef {SourceOrigin | DerivedOrigin | SyntheticOrigin} SemanticOrigin */
|
|
62
96
|
|
|
97
|
+
/**
|
|
98
|
+
* @typedef GeneratedByteRange
|
|
99
|
+
* @property {{start: number, end: number}} generated - Half-open generated byte offsets.
|
|
100
|
+
* @property {SemanticOrigin} origin - Semantic or explicit synthetic provenance.
|
|
101
|
+
* @property {string} [nodeId] - Originating semantic node identity.
|
|
102
|
+
* @property {string} [symbolId] - Originating semantic symbol identity.
|
|
103
|
+
* @property {string} [role] - Semantic or binary-region role.
|
|
104
|
+
*/
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @typedef SemantifoldByteMapping
|
|
108
|
+
* @property {"SemantifoldByteMapping"} schema - Schema discriminator.
|
|
109
|
+
* @property {1} version - Schema version.
|
|
110
|
+
* @property {"bytes"} coordinateSystem - Generated offsets are bytes, never text columns.
|
|
111
|
+
* @property {{path: string, byteLength: number}} generated - Generated artifact identity.
|
|
112
|
+
* @property {readonly GeneratedByteRange[]} ranges - Ordered non-overlapping ranges.
|
|
113
|
+
*/
|
|
114
|
+
|
|
63
115
|
/**
|
|
64
116
|
* @typedef SemanticNodeSourceProvenance
|
|
65
117
|
* @property {"SemantifoldNodeProvenance"} schema - Association discriminator.
|
|
@@ -147,6 +199,77 @@
|
|
|
147
199
|
* @property {string} sourceMapFilename - Default external Source Map filename.
|
|
148
200
|
*/
|
|
149
201
|
|
|
202
|
+
/**
|
|
203
|
+
* @typedef TextArtifactProvenance
|
|
204
|
+
* @property {"text"} kind - Rich text provenance discriminator.
|
|
205
|
+
* @property {SemantifoldMapping} mapping - Authoritative range mapping.
|
|
206
|
+
* @property {import("@jridgewell/gen-mapping").EncodedSourceMap} sourceMap - Interoperable projection.
|
|
207
|
+
* @property {string} [sourceMapFilename] - Logical Source Map sidecar path.
|
|
208
|
+
*/
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* @typedef ByteArtifactProvenance
|
|
212
|
+
* @property {"bytes"} kind - Byte provenance discriminator.
|
|
213
|
+
* @property {SemantifoldByteMapping} mapping - Authoritative byte ranges.
|
|
214
|
+
*/
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* @typedef ArtifactSyntheticProvenance
|
|
218
|
+
* @property {"synthetic"} kind - Synthetic artifact discriminator.
|
|
219
|
+
* @property {string} reason - Explicit target-scaffolding reason.
|
|
220
|
+
* @property {readonly RelatedOrigin[]} relatedOrigins - Related semantic source ranges.
|
|
221
|
+
*/
|
|
222
|
+
|
|
223
|
+
/** @typedef {TextArtifactProvenance | ByteArtifactProvenance | ArtifactSyntheticProvenance} ArtifactProvenance */
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* @typedef GeneratedSetArtifact
|
|
227
|
+
* @property {string} path - Unique safe relative POSIX path.
|
|
228
|
+
* @property {"text" | "binary"} contentKind - Content representation.
|
|
229
|
+
* @property {string | Uint8Array} content - Text or a detached copy of the exact generated bytes on every binary read.
|
|
230
|
+
* @property {string} mediaType - Artifact media type.
|
|
231
|
+
* @property {GeneratedArtifactRole} role - Artifact role; exactly one set artifact is entry.
|
|
232
|
+
* @property {"generated"} ownership - Task-015 ownership contract.
|
|
233
|
+
* @property {ArtifactProvenance} provenance - Text, byte-range, or synthetic provenance.
|
|
234
|
+
*/
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* @typedef GeneratedArtifactSet
|
|
238
|
+
* @property {"GeneratedArtifactSet"} schema - Schema discriminator.
|
|
239
|
+
* @property {1} version - Schema version.
|
|
240
|
+
* @property {string} target - Stable language or target ID.
|
|
241
|
+
* @property {string} entry - Path of the sole declared entry artifact.
|
|
242
|
+
* @property {readonly GeneratedSetArtifact[]} artifacts - Deterministic artifact order.
|
|
243
|
+
*/
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* @typedef DiscoveredToolchain
|
|
247
|
+
* @property {string} id - Toolchain ID.
|
|
248
|
+
* @property {string} command - Documented canonical command.
|
|
249
|
+
* @property {string} executable - Exact absolute executable path.
|
|
250
|
+
* @property {"override" | "canonical"} source - Resolution route.
|
|
251
|
+
* @property {string} version - First captured version line.
|
|
252
|
+
* @property {string} versionOutput - Complete captured version output.
|
|
253
|
+
* @property {readonly string[]} versionArguments - Exact version argument array.
|
|
254
|
+
*/
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* @typedef AcceptanceStageResult
|
|
258
|
+
* @property {AcceptanceStage} stage - Completed stage.
|
|
259
|
+
* @property {string} executable - Exact executable used.
|
|
260
|
+
* @property {readonly string[]} arguments - Exact argument array used.
|
|
261
|
+
* @property {string} version - Discovered tool version.
|
|
262
|
+
* @property {string} stdout - Captured standard output.
|
|
263
|
+
* @property {string} stderr - Captured standard error.
|
|
264
|
+
*/
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* @typedef AcceptanceResult
|
|
268
|
+
* @property {string} target - Target identity.
|
|
269
|
+
* @property {string} directory - Removed isolated project directory identity.
|
|
270
|
+
* @property {readonly AcceptanceStageResult[]} stages - Ordered completed stages.
|
|
271
|
+
*/
|
|
272
|
+
|
|
150
273
|
/**
|
|
151
274
|
* @typedef TypeReference
|
|
152
275
|
* @property {"TypeReference"} kind - Node discriminator.
|