semantifold 0.1.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/LICENSE +15 -0
- package/README.md +71 -0
- package/build/index.d.ts +54 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +45 -0
- package/build/src/backends/identifiers.d.ts +19 -0
- package/build/src/backends/identifiers.d.ts.map +1 -0
- package/build/src/backends/identifiers.js +93 -0
- package/build/src/backends/index.d.ts +35 -0
- package/build/src/backends/index.d.ts.map +1 -0
- package/build/src/backends/index.js +149 -0
- package/build/src/backends/java.d.ts +8 -0
- package/build/src/backends/java.d.ts.map +1 -0
- package/build/src/backends/java.js +172 -0
- package/build/src/backends/javascript.d.ts +8 -0
- package/build/src/backends/javascript.d.ts.map +1 -0
- package/build/src/backends/javascript.js +173 -0
- package/build/src/backends/php.d.ts +8 -0
- package/build/src/backends/php.d.ts.map +1 -0
- package/build/src/backends/php.js +175 -0
- package/build/src/backends/ruby.d.ts +8 -0
- package/build/src/backends/ruby.d.ts.map +1 -0
- package/build/src/backends/ruby.js +155 -0
- package/build/src/backends/scalars.d.ts +15 -0
- package/build/src/backends/scalars.d.ts.map +1 -0
- package/build/src/backends/scalars.js +138 -0
- package/build/src/backends/shared.d.ts +26 -0
- package/build/src/backends/shared.d.ts.map +1 -0
- package/build/src/backends/shared.js +424 -0
- package/build/src/backends/typescript.d.ts +8 -0
- package/build/src/backends/typescript.d.ts.map +1 -0
- package/build/src/backends/typescript.js +160 -0
- package/build/src/backends/writer.d.ts +87 -0
- package/build/src/backends/writer.d.ts.map +1 -0
- package/build/src/backends/writer.js +207 -0
- package/build/src/diagnostic.d.ts +67 -0
- package/build/src/diagnostic.d.ts.map +1 -0
- package/build/src/diagnostic.js +96 -0
- package/build/src/frontends/index.d.ts +14 -0
- package/build/src/frontends/index.d.ts.map +1 -0
- package/build/src/frontends/index.js +36 -0
- package/build/src/frontends/java.d.ts +12 -0
- package/build/src/frontends/java.d.ts.map +1 -0
- package/build/src/frontends/java.js +630 -0
- package/build/src/frontends/javascript-typescript.d.ts +19 -0
- package/build/src/frontends/javascript-typescript.d.ts.map +1 -0
- package/build/src/frontends/javascript-typescript.js +633 -0
- package/build/src/frontends/php.d.ts +12 -0
- package/build/src/frontends/php.d.ts.map +1 -0
- package/build/src/frontends/php.js +523 -0
- package/build/src/frontends/ruby.d.ts +12 -0
- package/build/src/frontends/ruby.d.ts.map +1 -0
- package/build/src/frontends/ruby.js +515 -0
- package/build/src/frontends/scalars.d.ts +19 -0
- package/build/src/frontends/scalars.d.ts.map +1 -0
- package/build/src/frontends/scalars.js +71 -0
- package/build/src/mapping.d.ts +160 -0
- package/build/src/mapping.d.ts.map +1 -0
- package/build/src/mapping.js +1120 -0
- package/build/src/semantic/location.d.ts +61 -0
- package/build/src/semantic/location.d.ts.map +1 -0
- package/build/src/semantic/location.js +142 -0
- package/build/src/semantic/operators.d.ts +16 -0
- package/build/src/semantic/operators.d.ts.map +1 -0
- package/build/src/semantic/operators.js +24 -0
- package/build/src/semantic/provenance.d.ts +74 -0
- package/build/src/semantic/provenance.d.ts.map +1 -0
- package/build/src/semantic/provenance.js +577 -0
- package/build/src/semantic/scalars.d.ts +19 -0
- package/build/src/semantic/scalars.d.ts.map +1 -0
- package/build/src/semantic/scalars.js +40 -0
- package/build/src/semantic/types.d.ts +1003 -0
- package/build/src/semantic/types.d.ts.map +1 -0
- package/build/src/semantic/types.js +279 -0
- package/build/src/semantic/validate.d.ts +60 -0
- package/build/src/semantic/validate.d.ts.map +1 -0
- package/build/src/semantic/validate.js +472 -0
- package/docs/architecture.md +39 -0
- package/docs/coding-standards.md +7 -0
- package/docs/goals.md +7 -0
- package/docs/language-support.md +69 -0
- package/docs/plans/2026-09-02-initial-toolchain.md +54 -0
- package/docs/source-maps.md +134 -0
- package/docs/testing.md +25 -0
- package/index.js +63 -0
- package/package.json +69 -0
- package/src/backends/identifiers.js +101 -0
- package/src/backends/index.js +159 -0
- package/src/backends/java.js +182 -0
- package/src/backends/javascript.js +184 -0
- package/src/backends/php.js +184 -0
- package/src/backends/ruby.js +163 -0
- package/src/backends/scalars.js +120 -0
- package/src/backends/shared.js +456 -0
- package/src/backends/typescript.js +168 -0
- package/src/backends/writer.js +224 -0
- package/src/diagnostic.js +103 -0
- package/src/frontends/index.js +36 -0
- package/src/frontends/java.js +735 -0
- package/src/frontends/javascript-typescript.js +718 -0
- package/src/frontends/php.js +593 -0
- package/src/frontends/ruby.js +622 -0
- package/src/frontends/scalars.js +77 -0
- package/src/mapping.js +1260 -0
- package/src/semantic/location.js +154 -0
- package/src/semantic/operators.js +28 -0
- package/src/semantic/provenance.js +629 -0
- package/src/semantic/scalars.js +44 -0
- package/src/semantic/types.js +319 -0
- package/src/semantic/validate.js +529 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import {createGenerationIndex} from "../semantic/provenance.js"
|
|
4
|
+
|
|
5
|
+
/** Source-aware deterministic generated-output builder. */
|
|
6
|
+
export class SourceWriter {
|
|
7
|
+
/**
|
|
8
|
+
* Creates a source-aware writer.
|
|
9
|
+
* @param {object} options - Writer options.
|
|
10
|
+
* @param {string} options.filename - Output filename.
|
|
11
|
+
* @param {import("../semantic/types.js").SemanticLanguage} options.language - Output language.
|
|
12
|
+
* @param {import("../semantic/types.js").SemanticModule} options.module - Semantic module.
|
|
13
|
+
* @param {{filename: string, content: string, language?: import("../semantic/types.js").SemanticLanguage}[]} [options.sources] - Caller-provided sources.
|
|
14
|
+
*/
|
|
15
|
+
constructor({filename, language, module, sources}) {
|
|
16
|
+
const index = createGenerationIndex(module, sources)
|
|
17
|
+
|
|
18
|
+
this.filename = filename
|
|
19
|
+
this.language = language
|
|
20
|
+
this.index = index
|
|
21
|
+
/** @type {string[]} */
|
|
22
|
+
this.parts = []
|
|
23
|
+
/** @type {import("../semantic/types.js").SemantifoldMappingSpan[]} */
|
|
24
|
+
this.spans = []
|
|
25
|
+
this.offset = 0
|
|
26
|
+
this.line = 1
|
|
27
|
+
this.column = 1
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Returns generated content so far.
|
|
32
|
+
* @returns {string} Generated content.
|
|
33
|
+
*/
|
|
34
|
+
get code() {
|
|
35
|
+
return this.parts.join("")
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Returns the current JSON Pointer for one unambiguous semantic occurrence.
|
|
40
|
+
* @param {import("../semantic/types.js").SemanticNode} node - Semantic node occurrence.
|
|
41
|
+
* @returns {string} Current occurrence path.
|
|
42
|
+
*/
|
|
43
|
+
occurrencePath(node) {
|
|
44
|
+
return this.index.recordFor(node).path
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Reports whether an exact parser-owned role range is available.
|
|
49
|
+
* @param {import("../semantic/types.js").SemanticNode} node - Semantic node occurrence.
|
|
50
|
+
* @param {string} path - JSON Pointer for a shared node occurrence.
|
|
51
|
+
* @param {string} role - Semantic token role.
|
|
52
|
+
* @returns {boolean} Whether the role has an exact source range.
|
|
53
|
+
*/
|
|
54
|
+
hasRange(node, path, role) {
|
|
55
|
+
return this.index.recordFor(node, path).ranges[role] !== undefined
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Writes text mapped to a semantic node.
|
|
60
|
+
* @param {string} text - Generated text.
|
|
61
|
+
* @param {object} annotation - Mapping annotation.
|
|
62
|
+
* @param {import("../semantic/types.js").SemanticNode} annotation.node - Semantic node.
|
|
63
|
+
* @param {"exact" | "anchor"} annotation.mappingKind - Mapping precision.
|
|
64
|
+
* @param {string} [annotation.role] - Semantic token role.
|
|
65
|
+
* @param {string} [annotation.name] - Source Map name.
|
|
66
|
+
* @param {string} [annotation.path] - JSON Pointer for a shared node occurrence.
|
|
67
|
+
* @returns {void}
|
|
68
|
+
*/
|
|
69
|
+
mapped(text, {mappingKind, name, node, path, role}) {
|
|
70
|
+
if (text.length == 0) return
|
|
71
|
+
|
|
72
|
+
const record = this.index.recordFor(node, path)
|
|
73
|
+
const roleLocation = role ? record.ranges[role] : undefined
|
|
74
|
+
const origin = roleLocation ? this.#sourceOrigin(roleLocation, record.origin) : record.origin
|
|
75
|
+
const precision = mappingKind == "exact" && role && !roleLocation ? "anchor" : mappingKind
|
|
76
|
+
const symbol = record.symbolId
|
|
77
|
+
? this.index.provenance.symbols.find((candidate) => candidate.id == record.symbolId)
|
|
78
|
+
: undefined
|
|
79
|
+
|
|
80
|
+
this.#append(text, {
|
|
81
|
+
mappingKind: precision,
|
|
82
|
+
name: name ?? symbol?.name,
|
|
83
|
+
nodeId: record.id,
|
|
84
|
+
origin,
|
|
85
|
+
role,
|
|
86
|
+
symbolId: record.symbolId
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Writes source-free scaffolding with optional semantic context.
|
|
92
|
+
* @param {string} text - Generated text.
|
|
93
|
+
* @param {string} reason - Stable reason.
|
|
94
|
+
* @param {import("../semantic/types.js").SemanticNode[]} [relatedNodes] - Related nodes.
|
|
95
|
+
* @param {(string | undefined)[]} [relatedPaths] - Occurrence paths for shared related nodes.
|
|
96
|
+
* @returns {void}
|
|
97
|
+
*/
|
|
98
|
+
synthetic(text, reason, relatedNodes = [], relatedPaths = []) {
|
|
99
|
+
if (text.length == 0) return
|
|
100
|
+
|
|
101
|
+
/** @type {Set<string>} */
|
|
102
|
+
const seen = new Set()
|
|
103
|
+
const relatedOrigins = relatedNodes.flatMap((node, index) => {
|
|
104
|
+
const record = this.index.recordFor(node, relatedPaths[index])
|
|
105
|
+
|
|
106
|
+
return relatedOriginsFor(record.origin).flatMap((related) => {
|
|
107
|
+
const contextual = {
|
|
108
|
+
...related,
|
|
109
|
+
nodeId: related.nodeId ?? record.id,
|
|
110
|
+
role: related.role ?? "context"
|
|
111
|
+
}
|
|
112
|
+
const key = relatedOriginKey(contextual)
|
|
113
|
+
|
|
114
|
+
if (seen.has(key)) return []
|
|
115
|
+
seen.add(key)
|
|
116
|
+
|
|
117
|
+
return [contextual]
|
|
118
|
+
})
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
this.#append(text, {mappingKind: "synthetic", origin: {kind: "synthetic", reason, relatedOrigins}})
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Returns the authoritative rich map for the current content.
|
|
126
|
+
* @returns {import("../semantic/types.js").SemantifoldMapping} Mapping.
|
|
127
|
+
*/
|
|
128
|
+
finish() {
|
|
129
|
+
return {
|
|
130
|
+
coordinateSystem: "utf16",
|
|
131
|
+
generated: {content: this.code, filename: this.filename, language: this.language},
|
|
132
|
+
nodes: [...this.index.provenance.nodes],
|
|
133
|
+
schema: "SemantifoldMapping",
|
|
134
|
+
sources: [...this.index.provenance.sources],
|
|
135
|
+
spans: [...this.spans],
|
|
136
|
+
symbols: [...this.index.provenance.symbols],
|
|
137
|
+
version: 1
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Appends one mapped span.
|
|
143
|
+
* @param {string} text - Generated text.
|
|
144
|
+
* @param {Omit<import("../semantic/types.js").SemantifoldMappingSpan, "generated">} annotation - Span annotation.
|
|
145
|
+
* @returns {void}
|
|
146
|
+
*/
|
|
147
|
+
#append(text, annotation) {
|
|
148
|
+
if (text.includes("\r")) throw new TypeError("Generated output must use deterministic LF line endings.")
|
|
149
|
+
|
|
150
|
+
const start = {column: this.column, line: this.line, offset: this.offset}
|
|
151
|
+
|
|
152
|
+
this.parts.push(text)
|
|
153
|
+
for (let index = 0; index < text.length; index++) {
|
|
154
|
+
this.offset++
|
|
155
|
+
if (text[index] == "\n") {
|
|
156
|
+
this.line++
|
|
157
|
+
this.column = 1
|
|
158
|
+
} else this.column++
|
|
159
|
+
}
|
|
160
|
+
const compact = /** @type {Omit<import("../semantic/types.js").SemantifoldMappingSpan, "generated">} */ (
|
|
161
|
+
Object.fromEntries(Object.entries(annotation).filter(([, value]) => value !== undefined))
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
this.spans.push({
|
|
165
|
+
...compact,
|
|
166
|
+
generated: {
|
|
167
|
+
end: {column: this.column, line: this.line, offset: this.offset},
|
|
168
|
+
filename: this.filename,
|
|
169
|
+
start
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Creates a direct role origin using canonical source identities.
|
|
176
|
+
* @param {import("../semantic/types.js").SourceLocation} location - Exact role range.
|
|
177
|
+
* @param {import("../semantic/types.js").SemanticOrigin} fallback - Owning origin.
|
|
178
|
+
* @returns {import("../semantic/types.js").SemanticOrigin} Role origin.
|
|
179
|
+
*/
|
|
180
|
+
#sourceOrigin(location, fallback) {
|
|
181
|
+
/** @type {string | undefined} */
|
|
182
|
+
let fallbackSourceId
|
|
183
|
+
|
|
184
|
+
if (fallback.kind == "source" && fallback.location.filename == location.filename) fallbackSourceId = fallback.sourceId
|
|
185
|
+
else if (fallback.kind == "derived") {
|
|
186
|
+
fallbackSourceId = fallback.origins.find((origin) => origin.location.filename == location.filename)?.sourceId
|
|
187
|
+
} else if (fallback.kind == "synthetic") {
|
|
188
|
+
fallbackSourceId = fallback.relatedOrigins.find((origin) => origin.location.filename == location.filename)?.sourceId
|
|
189
|
+
}
|
|
190
|
+
const source = this.index.provenance.sources.find((candidate) => candidate.id == fallbackSourceId) ??
|
|
191
|
+
this.index.provenance.sources.find((candidate) => candidate.filename == location.filename)
|
|
192
|
+
|
|
193
|
+
return source ? {kind: "source", location, sourceId: source.id} : fallback
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Expands every closed provenance range in semantic order.
|
|
199
|
+
* @param {import("../semantic/types.js").SemanticOrigin} origin - Closed provenance.
|
|
200
|
+
* @returns {import("../semantic/types.js").RelatedOrigin[]} Related ranges.
|
|
201
|
+
*/
|
|
202
|
+
function relatedOriginsFor(origin) {
|
|
203
|
+
if (origin.kind == "source") return [{location: origin.location, sourceId: origin.sourceId}]
|
|
204
|
+
if (origin.kind == "derived") return origin.origins
|
|
205
|
+
|
|
206
|
+
return origin.relatedOrigins
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Builds a deterministic identity for one contextual related origin.
|
|
211
|
+
* @param {import("../semantic/types.js").RelatedOrigin} origin - Related origin.
|
|
212
|
+
* @returns {string} Stable identity.
|
|
213
|
+
*/
|
|
214
|
+
function relatedOriginKey(origin) {
|
|
215
|
+
return JSON.stringify([
|
|
216
|
+
origin.sourceId,
|
|
217
|
+
origin.location.filename,
|
|
218
|
+
origin.location.start.offset,
|
|
219
|
+
origin.location.end.offset,
|
|
220
|
+
origin.nodeId ?? null,
|
|
221
|
+
origin.symbolId ?? null,
|
|
222
|
+
origin.role ?? null
|
|
223
|
+
])
|
|
224
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/** A clear parser, semantic, or backend capability diagnostic. */
|
|
4
|
+
export class SemantifoldDiagnostic extends Error {
|
|
5
|
+
/**
|
|
6
|
+
* Creates a diagnostic.
|
|
7
|
+
* @param {object} options - Diagnostic fields.
|
|
8
|
+
* @param {string} options.code - Stable diagnostic code.
|
|
9
|
+
* @param {string} options.message - Human-readable message.
|
|
10
|
+
* @param {import("./semantic/types.js").SemanticLanguage} options.language - Related language.
|
|
11
|
+
* @param {import("./semantic/types.js").SourceLocation | undefined} [options.location] - Related location.
|
|
12
|
+
* @param {import("./semantic/types.js").SourceLocation | undefined} [options.generatedLocation] - Pre-remapping generated location.
|
|
13
|
+
* @param {Error | undefined} [options.cause] - Preserved parser error.
|
|
14
|
+
*/
|
|
15
|
+
constructor({cause, code, generatedLocation, language, location, message}) {
|
|
16
|
+
const locationText = location ? ` at ${location.filename}:${location.start.line}:${location.start.column}` : ""
|
|
17
|
+
|
|
18
|
+
super(`[${code}] ${language}${locationText}: ${message}`, cause ? {cause} : undefined)
|
|
19
|
+
this.name = "SemantifoldDiagnostic"
|
|
20
|
+
this.code = code
|
|
21
|
+
this.detail = message
|
|
22
|
+
this.generatedLocation = generatedLocation
|
|
23
|
+
this.language = language
|
|
24
|
+
this.location = location
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Throws a normalized parser diagnostic while preserving the parser error.
|
|
30
|
+
* @param {import("./semantic/types.js").SemanticLanguage} language - Frontend language.
|
|
31
|
+
* @param {unknown} error - Opaque parser error, narrowed immediately.
|
|
32
|
+
* @returns {never} Always throws.
|
|
33
|
+
*/
|
|
34
|
+
export function parseFailure(language, error) {
|
|
35
|
+
const cause = error instanceof Error ? error : new Error(String(error))
|
|
36
|
+
|
|
37
|
+
throw new SemantifoldDiagnostic({
|
|
38
|
+
cause,
|
|
39
|
+
code: "PARSE_ERROR",
|
|
40
|
+
language,
|
|
41
|
+
message: cause.message
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Throws an unsupported-syntax diagnostic.
|
|
47
|
+
* @param {import("./semantic/types.js").SemanticLanguage} language - Frontend language.
|
|
48
|
+
* @param {string} syntax - Parser syntax kind.
|
|
49
|
+
* @param {import("./semantic/types.js").SourceLocation | undefined} location - Source location.
|
|
50
|
+
* @returns {never} Always throws.
|
|
51
|
+
*/
|
|
52
|
+
export function unsupportedSyntax(language, syntax, location) {
|
|
53
|
+
throw new SemantifoldDiagnostic({
|
|
54
|
+
code: "UNSUPPORTED_SYNTAX",
|
|
55
|
+
language,
|
|
56
|
+
location,
|
|
57
|
+
message: `Syntax '${syntax}' is outside the implemented semantic subset.`
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Throws a missing-type diagnostic.
|
|
63
|
+
* @param {import("./semantic/types.js").SemanticLanguage} language - Frontend language.
|
|
64
|
+
* @param {string} subject - Untyped subject.
|
|
65
|
+
* @param {import("./semantic/types.js").SourceLocation | undefined} location - Source location.
|
|
66
|
+
* @returns {never} Always throws.
|
|
67
|
+
*/
|
|
68
|
+
export function missingType(language, subject, location) {
|
|
69
|
+
throw new SemantifoldDiagnostic({
|
|
70
|
+
code: "MISSING_TYPE",
|
|
71
|
+
language,
|
|
72
|
+
location,
|
|
73
|
+
message: `${subject} requires an explicit supported scalar type: integer, boolean, or string.`
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Throws a language-neutral semantic diagnostic discovered after adaptation.
|
|
79
|
+
* @param {import("./semantic/types.js").SemanticLanguage} language - Source language.
|
|
80
|
+
* @param {string} code - Stable semantic diagnostic code.
|
|
81
|
+
* @param {string} detail - Human-readable semantic failure.
|
|
82
|
+
* @param {import("./semantic/types.js").SourceLocation | undefined} location - Source location.
|
|
83
|
+
* @returns {never} Always throws.
|
|
84
|
+
*/
|
|
85
|
+
export function semanticFailure(language, code, detail, location) {
|
|
86
|
+
throw new SemantifoldDiagnostic({code, language, location, message: detail})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Throws an unsupported backend-capability diagnostic.
|
|
91
|
+
* @param {import("./semantic/types.js").SemanticLanguage} language - Backend language.
|
|
92
|
+
* @param {string} capability - Unsupported semantic capability.
|
|
93
|
+
* @param {import("./semantic/types.js").SourceLocation | undefined} location - Semantic source location.
|
|
94
|
+
* @returns {never} Always throws.
|
|
95
|
+
*/
|
|
96
|
+
export function unsupportedCapability(language, capability, location) {
|
|
97
|
+
throw new SemantifoldDiagnostic({
|
|
98
|
+
code: "UNSUPPORTED_CAPABILITY",
|
|
99
|
+
language,
|
|
100
|
+
location,
|
|
101
|
+
message: `Backend cannot emit semantic capability '${capability}'.`
|
|
102
|
+
})
|
|
103
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import {SemantifoldDiagnostic} from "../diagnostic.js"
|
|
4
|
+
import {annotateParsedModule} from "../semantic/provenance.js"
|
|
5
|
+
import {validateParsedModule} from "../semantic/validate.js"
|
|
6
|
+
import {parseJava} from "./java.js"
|
|
7
|
+
import {parseJavaScriptTypeScript} from "./javascript-typescript.js"
|
|
8
|
+
import {parsePhp} from "./php.js"
|
|
9
|
+
import {parseRuby} from "./ruby.js"
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Parses supported source into a shared semantic module.
|
|
13
|
+
* @param {object} input - Parse request.
|
|
14
|
+
* @param {string} input.filename - Source filename used in diagnostics.
|
|
15
|
+
* @param {import("../semantic/types.js").SemanticLanguage} input.language - Source language.
|
|
16
|
+
* @param {string} input.source - Source text.
|
|
17
|
+
* @returns {import("../semantic/types.js").SemanticModule} Semantic module.
|
|
18
|
+
*/
|
|
19
|
+
export function parseSource({filename, language, source}) {
|
|
20
|
+
let module
|
|
21
|
+
|
|
22
|
+
if (language == "php") module = validateParsedModule(parsePhp({filename, source}), language)
|
|
23
|
+
else if (language == "ruby") module = validateParsedModule(parseRuby({filename, source}), language)
|
|
24
|
+
if (language == "javascript" || language == "typescript") {
|
|
25
|
+
module = validateParsedModule(parseJavaScriptTypeScript({filename, language, source}), language)
|
|
26
|
+
}
|
|
27
|
+
else if (language == "java") module = validateParsedModule(parseJava({filename, source}), language)
|
|
28
|
+
|
|
29
|
+
if (module) return annotateParsedModule(module, {filename, language, source})
|
|
30
|
+
|
|
31
|
+
throw new SemantifoldDiagnostic({
|
|
32
|
+
code: "UNSUPPORTED_LANGUAGE",
|
|
33
|
+
language,
|
|
34
|
+
message: "No frontend adapter is registered for this language."
|
|
35
|
+
})
|
|
36
|
+
}
|