semantifold 0.9.0 → 0.11.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 +6 -3
- package/bin/semantifold.js +6 -0
- package/build/bin/semantifold.d.ts +3 -0
- package/build/bin/semantifold.d.ts.map +1 -0
- package/build/bin/semantifold.js +5 -0
- package/build/index.d.ts +8 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +9 -1
- package/build/src/artifact-path.d.ts +16 -0
- package/build/src/artifact-path.d.ts.map +1 -1
- package/build/src/artifact-path.js +29 -3
- package/build/src/backends/c-check.d.ts +7 -0
- package/build/src/backends/c-check.d.ts.map +1 -0
- package/build/src/backends/c-check.js +65 -0
- package/build/src/backends/check-plan.d.ts +96 -0
- package/build/src/backends/check-plan.d.ts.map +1 -0
- package/build/src/backends/check-plan.js +135 -0
- package/build/src/backends/cpp-check.d.ts +7 -0
- package/build/src/backends/cpp-check.d.ts.map +1 -0
- package/build/src/backends/cpp-check.js +59 -0
- package/build/src/backends/csharp-check.d.ts +7 -0
- package/build/src/backends/csharp-check.d.ts.map +1 -0
- package/build/src/backends/csharp-check.js +125 -0
- package/build/src/backends/csharp.d.ts +1 -0
- package/build/src/backends/csharp.d.ts.map +1 -1
- package/build/src/backends/csharp.js +5 -4
- package/build/src/backends/dart-check.d.ts +7 -0
- package/build/src/backends/dart-check.d.ts.map +1 -0
- package/build/src/backends/dart-check.js +103 -0
- package/build/src/backends/dart.d.ts +2 -0
- package/build/src/backends/dart.d.ts.map +1 -1
- package/build/src/backends/dart.js +5 -5
- package/build/src/backends/go-check.d.ts +7 -0
- package/build/src/backends/go-check.d.ts.map +1 -0
- package/build/src/backends/go-check.js +77 -0
- package/build/src/backends/go.d.ts +1 -0
- package/build/src/backends/go.d.ts.map +1 -1
- package/build/src/backends/go.js +3 -3
- package/build/src/backends/java-check.d.ts +21 -0
- package/build/src/backends/java-check.d.ts.map +1 -0
- package/build/src/backends/java-check.js +73 -0
- package/build/src/backends/javascript-check.d.ts +7 -0
- package/build/src/backends/javascript-check.d.ts.map +1 -0
- package/build/src/backends/javascript-check.js +29 -0
- package/build/src/backends/kotlin-check.d.ts +7 -0
- package/build/src/backends/kotlin-check.d.ts.map +1 -0
- package/build/src/backends/kotlin-check.js +31 -0
- package/build/src/backends/kotlin.d.ts +1 -1
- package/build/src/backends/kotlin.js +2 -2
- package/build/src/backends/php-check.d.ts +7 -0
- package/build/src/backends/php-check.d.ts.map +1 -0
- package/build/src/backends/php-check.js +21 -0
- package/build/src/backends/program.d.ts +7 -0
- package/build/src/backends/program.d.ts.map +1 -1
- package/build/src/backends/program.js +16 -1
- package/build/src/backends/python-check.d.ts +7 -0
- package/build/src/backends/python-check.d.ts.map +1 -0
- package/build/src/backends/python-check.js +28 -0
- package/build/src/backends/ruby-check.d.ts +7 -0
- package/build/src/backends/ruby-check.d.ts.map +1 -0
- package/build/src/backends/ruby-check.js +21 -0
- package/build/src/backends/rust-check.d.ts +7 -0
- package/build/src/backends/rust-check.d.ts.map +1 -0
- package/build/src/backends/rust-check.js +65 -0
- package/build/src/backends/shared.d.ts.map +1 -1
- package/build/src/backends/shared.js +29 -1
- package/build/src/backends/swift-check.d.ts +7 -0
- package/build/src/backends/swift-check.d.ts.map +1 -0
- package/build/src/backends/swift-check.js +77 -0
- package/build/src/backends/typescript-check.d.ts +7 -0
- package/build/src/backends/typescript-check.d.ts.map +1 -0
- package/build/src/backends/typescript-check.js +33 -0
- package/build/src/backends/zig-check.d.ts +7 -0
- package/build/src/backends/zig-check.d.ts.map +1 -0
- package/build/src/backends/zig-check.js +70 -0
- package/build/src/diagnostic.d.ts +13 -1
- package/build/src/diagnostic.d.ts.map +1 -1
- package/build/src/diagnostic.js +14 -2
- package/build/src/frontends/java.d.ts.map +1 -1
- package/build/src/frontends/java.js +23 -1
- package/build/src/frontends/javascript-typescript.d.ts.map +1 -1
- package/build/src/frontends/javascript-typescript.js +45 -4
- package/build/src/frontends/php.d.ts.map +1 -1
- package/build/src/frontends/php.js +54 -3
- package/build/src/frontends/program.d.ts +6 -0
- package/build/src/frontends/program.d.ts.map +1 -1
- package/build/src/frontends/program.js +9 -1
- package/build/src/frontends/ruby.d.ts.map +1 -1
- package/build/src/frontends/ruby.js +55 -5
- package/build/src/language-registry.d.ts +54 -0
- package/build/src/language-registry.d.ts.map +1 -1
- package/build/src/language-registry.js +124 -10
- package/build/src/project-build.d.ts +172 -0
- package/build/src/project-build.d.ts.map +1 -0
- package/build/src/project-build.js +320 -0
- package/build/src/project-cli.d.ts +40 -0
- package/build/src/project-cli.d.ts.map +1 -0
- package/build/src/project-cli.js +145 -0
- package/build/src/project-manifest.d.ts +148 -0
- package/build/src/project-manifest.d.ts.map +1 -0
- package/build/src/project-manifest.js +432 -0
- package/build/src/project-reporter.d.ts +81 -0
- package/build/src/project-reporter.d.ts.map +1 -0
- package/build/src/project-reporter.js +293 -0
- package/build/src/project-snapshot.d.ts +129 -0
- package/build/src/project-snapshot.d.ts.map +1 -0
- package/build/src/project-snapshot.js +362 -0
- package/build/src/project-watch-reporter.d.ts +125 -0
- package/build/src/project-watch-reporter.d.ts.map +1 -0
- package/build/src/project-watch-reporter.js +280 -0
- package/build/src/project-watch.d.ts +133 -0
- package/build/src/project-watch.d.ts.map +1 -0
- package/build/src/project-watch.js +839 -0
- package/build/src/publication.d.ts +69 -5
- package/build/src/publication.d.ts.map +1 -1
- package/build/src/publication.js +246 -57
- package/build/src/semantic/effects.js +3 -1
- package/build/src/semantic/provenance.d.ts.map +1 -1
- package/build/src/semantic/provenance.js +11 -1
- package/build/src/semantic/types.d.ts +355 -4
- package/build/src/semantic/types.d.ts.map +1 -1
- package/build/src/semantic/types.js +92 -3
- package/build/src/semantic/validate.js +18 -1
- package/build/src/subprocess.d.ts +8 -0
- package/build/src/subprocess.d.ts.map +1 -1
- package/build/src/subprocess.js +50 -9
- package/build/src/target-check.d.ts +52 -0
- package/build/src/target-check.d.ts.map +1 -0
- package/build/src/target-check.js +796 -0
- package/build/src/toolchains.d.ts.map +1 -1
- package/build/src/toolchains.js +3 -3
- package/docs/adr-expression-breadth.md +186 -0
- package/docs/architecture.md +3 -1
- package/docs/c.md +4 -0
- package/docs/cpp.md +4 -0
- package/docs/dart.md +4 -0
- package/docs/generated-artifact-publication.md +11 -8
- package/docs/kotlin.md +2 -2
- package/docs/language-support.md +40 -6
- package/docs/parser-qualification.md +1 -1
- package/docs/project-build-cli.md +147 -0
- package/docs/project-watch.md +60 -0
- package/docs/rust.md +4 -0
- package/docs/swift.md +2 -0
- package/docs/testing.md +70 -3
- package/docs/watch-build-pipeline.md +39 -23
- package/docs/zig.md +2 -0
- package/examples/jsdoc-java-watch/expected-output.txt +3 -0
- package/examples/jsdoc-java-watch/semantifold.json +23 -0
- package/examples/jsdoc-java-watch/src/main.js +21 -0
- package/index.js +8 -0
- package/package.json +6 -1
- package/src/artifact-path.js +31 -2
- package/src/backends/c-check.js +80 -0
- package/src/backends/check-plan.js +147 -0
- package/src/backends/cpp-check.js +73 -0
- package/src/backends/csharp-check.js +139 -0
- package/src/backends/csharp.js +4 -3
- package/src/backends/dart-check.js +117 -0
- package/src/backends/dart.js +4 -4
- package/src/backends/go-check.js +89 -0
- package/src/backends/go.js +2 -2
- package/src/backends/java-check.js +77 -0
- package/src/backends/javascript-check.js +33 -0
- package/src/backends/kotlin-check.js +41 -0
- package/src/backends/kotlin.js +1 -1
- package/src/backends/php-check.js +23 -0
- package/src/backends/program.js +17 -0
- package/src/backends/python-check.js +30 -0
- package/src/backends/ruby-check.js +23 -0
- package/src/backends/rust-check.js +78 -0
- package/src/backends/shared.js +30 -0
- package/src/backends/swift-check.js +89 -0
- package/src/backends/typescript-check.js +37 -0
- package/src/backends/zig-check.js +81 -0
- package/src/diagnostic.js +9 -1
- package/src/frontends/java.js +24 -0
- package/src/frontends/javascript-typescript.js +49 -2
- package/src/frontends/php.js +61 -2
- package/src/frontends/program.js +9 -0
- package/src/frontends/ruby.js +62 -2
- package/src/language-registry.js +130 -9
- package/src/project-build.js +353 -0
- package/src/project-cli.js +155 -0
- package/src/project-manifest.js +479 -0
- package/src/project-reporter.js +320 -0
- package/src/project-snapshot.js +403 -0
- package/src/project-watch-reporter.js +303 -0
- package/src/project-watch.js +870 -0
- package/src/publication.js +275 -58
- package/src/semantic/effects.js +1 -0
- package/src/semantic/provenance.js +8 -0
- package/src/semantic/types.js +101 -2
- package/src/semantic/validate.js +26 -0
- package/src/subprocess.js +49 -8
- package/src/target-check.js +844 -0
- package/src/toolchains.js +3 -2
- package/tsconfig.json +1 -1
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import path from "node:path"
|
|
4
|
+
import {checkArtifacts, checkOutput, checkPlan, checkStage, checkTool, pathArgument} from "./check-plan.js"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Constructs Python's isolated bytecode compiler plan.
|
|
8
|
+
* @param {import("../language-registry.js").TargetCheckPlanContext} context - Validated candidate context.
|
|
9
|
+
* @returns {import("../semantic/types.js").TargetCheckPlan} Immutable Python plan.
|
|
10
|
+
*/
|
|
11
|
+
export function createPythonCheckPlan(context) {
|
|
12
|
+
const tool = checkTool(context, "python", "python")
|
|
13
|
+
const artifacts = checkArtifacts(context, artifact => artifact.path.endsWith(".py"), "python", "only staged '.py' artifacts")
|
|
14
|
+
const compiler = "import py_compile,sys;[py_compile.compile(sys.argv[index],cfile=sys.argv[index+1],dfile=sys.argv[index+2],doraise=True,invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH) for index in range(1,len(sys.argv),3)]"
|
|
15
|
+
const arguments_ = artifacts.flatMap(artifactPath => [
|
|
16
|
+
artifactPath,
|
|
17
|
+
path.join(context.buildPath, `${path.basename(artifactPath, ".py")}.pyc`),
|
|
18
|
+
path.basename(artifactPath)
|
|
19
|
+
])
|
|
20
|
+
|
|
21
|
+
return checkPlan(context, "python", artifacts, [checkStage({
|
|
22
|
+
argv: ["-I", "-B", "-c", compiler, ...arguments_],
|
|
23
|
+
cwd: context.sourcePath,
|
|
24
|
+
inputs: artifacts,
|
|
25
|
+
output: checkOutput(context.buildPath, "application/x-python-bytecode", "compiler-output"),
|
|
26
|
+
pathArguments: artifacts.flatMap((_, index) => [pathArgument(4 + index * 3, "source"), pathArgument(5 + index * 3, "build")]),
|
|
27
|
+
stage: "compile",
|
|
28
|
+
tool
|
|
29
|
+
})])
|
|
30
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import {checkArtifacts, checkPlan, checkStage, checkTool, pathArgument} from "./check-plan.js"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Constructs Ruby's non-executing syntax-check plan.
|
|
7
|
+
* @param {import("../language-registry.js").TargetCheckPlanContext} context - Validated candidate context.
|
|
8
|
+
* @returns {import("../semantic/types.js").TargetCheckPlan} Immutable Ruby plan.
|
|
9
|
+
*/
|
|
10
|
+
export function createRubyCheckPlan(context) {
|
|
11
|
+
const tool = checkTool(context, "ruby", "ruby")
|
|
12
|
+
const artifacts = checkArtifacts(context, artifact => artifact.path.endsWith(".rb"), "ruby", "only staged '.rb' artifacts")
|
|
13
|
+
|
|
14
|
+
return checkPlan(context, "ruby", artifacts, artifacts.map(artifact => checkStage({
|
|
15
|
+
argv: ["--disable=gems", "-c", artifact],
|
|
16
|
+
cwd: context.sourcePath,
|
|
17
|
+
inputs: [artifact],
|
|
18
|
+
output: null,
|
|
19
|
+
pathArguments: [pathArgument(2, "source")],
|
|
20
|
+
stage: "validate",
|
|
21
|
+
tool
|
|
22
|
+
})))
|
|
23
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import path from "node:path"
|
|
4
|
+
import {SemantifoldDiagnostic} from "../diagnostic.js"
|
|
5
|
+
import {
|
|
6
|
+
checkArtifacts,
|
|
7
|
+
checkPlan,
|
|
8
|
+
checkStage,
|
|
9
|
+
checkTool,
|
|
10
|
+
environmentPath,
|
|
11
|
+
environmentTool,
|
|
12
|
+
pathArgument
|
|
13
|
+
} from "./check-plan.js"
|
|
14
|
+
import {rustLockfile, rustManifest} from "./rust-runtime.js"
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Constructs Rust's locked offline Cargo build/check plan.
|
|
18
|
+
* @param {import("../language-registry.js").TargetCheckPlanContext} context - Validated candidate context.
|
|
19
|
+
* @returns {import("../semantic/types.js").TargetCheckPlan} Immutable Cargo plan.
|
|
20
|
+
*/
|
|
21
|
+
export function createRustCheckPlan(context) {
|
|
22
|
+
const rustc = checkTool(context, "rustc", "rust")
|
|
23
|
+
const cargo = checkTool(context, "cargo", "rust")
|
|
24
|
+
const artifacts = checkArtifacts(context, artifact => artifact.path.endsWith(".rs") ||
|
|
25
|
+
artifact.path == "Cargo.toml" || artifact.path == "Cargo.lock",
|
|
26
|
+
"rust", "only staged Rust source and canonical Cargo manifest/lock artifacts")
|
|
27
|
+
const manifest = context.artifacts.artifacts.filter(artifact => artifact.path == "Cargo.toml")
|
|
28
|
+
const lock = context.artifacts.artifacts.filter(artifact => artifact.path == "Cargo.lock")
|
|
29
|
+
const sources = artifacts.filter(artifact => artifact.endsWith(".rs"))
|
|
30
|
+
|
|
31
|
+
if (manifest.length != 1 || manifest[0].contentKind != "text" || manifest[0].role != "manifest" ||
|
|
32
|
+
manifest[0].content != rustManifest || lock.length != 1 || lock[0].contentKind != "text" ||
|
|
33
|
+
lock[0].content != rustLockfile || artifacts.length != 3 || sources.length != 1 ||
|
|
34
|
+
sources[0] != path.join(context.sourcePath, "src/main.rs")) {
|
|
35
|
+
throw new SemantifoldDiagnostic({
|
|
36
|
+
code: "INVALID_TARGET_CHECK_PLAN",
|
|
37
|
+
language: "rust",
|
|
38
|
+
message: "Rust developer checks require the exact dependency-free Cargo project/lock and staged '.rs' source."
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
const cargoHome = path.join(context.buildPath, "cargo-home")
|
|
42
|
+
const home = path.join(context.buildPath, "home")
|
|
43
|
+
const temporary = path.join(context.buildPath, "tmp")
|
|
44
|
+
const target = path.join(context.buildPath, "cargo-target")
|
|
45
|
+
const environment = {
|
|
46
|
+
CARGO_HOME: cargoHome,
|
|
47
|
+
CARGO_INCREMENTAL: "0",
|
|
48
|
+
CARGO_NET_OFFLINE: "true",
|
|
49
|
+
CARGO_TERM_COLOR: "never",
|
|
50
|
+
HOME: home,
|
|
51
|
+
PATH: [path.dirname(rustc.executable), path.dirname(cargo.executable), "/usr/local/bin", "/usr/bin", "/bin"].filter((value, index, values) =>
|
|
52
|
+
values.indexOf(value) == index).join(path.delimiter),
|
|
53
|
+
RUSTC: rustc.executable,
|
|
54
|
+
TMPDIR: temporary
|
|
55
|
+
}
|
|
56
|
+
const environmentPaths = [environmentPath("CARGO_HOME", "build"), environmentPath("HOME", "build"),
|
|
57
|
+
environmentTool("RUSTC", rustc), environmentPath("TMPDIR", "build")]
|
|
58
|
+
/**
|
|
59
|
+
* Constructs one Cargo stage over the complete generated project.
|
|
60
|
+
* @param {import("../semantic/types.js").AcceptanceStage} name - Registry stage identity.
|
|
61
|
+
* @param {string} command - Exact Cargo subcommand.
|
|
62
|
+
* @returns {import("../semantic/types.js").TargetCheckStageRequest} Immutable stage.
|
|
63
|
+
*/
|
|
64
|
+
const stage = (name, command) => checkStage({
|
|
65
|
+
argv: [command, "--offline", "--locked", "--target-dir", target],
|
|
66
|
+
cwd: context.sourcePath,
|
|
67
|
+
environment,
|
|
68
|
+
environmentPaths,
|
|
69
|
+
inputs: artifacts,
|
|
70
|
+
output: null,
|
|
71
|
+
pathArguments: [pathArgument(4, "build")],
|
|
72
|
+
stage: name,
|
|
73
|
+
tool: cargo,
|
|
74
|
+
transientPaths: [target]
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
return checkPlan(context, "rust", artifacts, [stage("compile", "build"), stage("validate", "check")])
|
|
78
|
+
}
|
package/src/backends/shared.js
CHANGED
|
@@ -37,6 +37,7 @@ const task012Languages = new Set(["php", "ruby", "javascript", "typescript", "ja
|
|
|
37
37
|
const task014Languages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
38
38
|
const task032Languages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
39
39
|
const task033Languages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
40
|
+
const task048ConditionalLanguages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
40
41
|
const javaReferenceMethodHooks = new Set(["clone", "equals", "finalize", "getClass", "hashCode", "notify", "notifyAll", "toString", "wait"])
|
|
41
42
|
const javascriptReferenceMethodHooks = new Set([
|
|
42
43
|
"__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "__proto__", "constructor",
|
|
@@ -1129,6 +1130,11 @@ function expressionContainsKind(expression, kind) {
|
|
|
1129
1130
|
if (expression.kind == "BinaryExpression") {
|
|
1130
1131
|
return expressionContainsKind(expression.left, kind) || expressionContainsKind(expression.right, kind)
|
|
1131
1132
|
}
|
|
1133
|
+
if (expression.kind == "ConditionalExpression") {
|
|
1134
|
+
return expressionContainsKind(expression.condition, kind) ||
|
|
1135
|
+
expressionContainsKind(expression.consequent, kind) ||
|
|
1136
|
+
expressionContainsKind(expression.alternate, kind)
|
|
1137
|
+
}
|
|
1132
1138
|
if (expression.kind == "CallExpression") return expression.arguments.some((argument) => expressionContainsKind(argument, kind))
|
|
1133
1139
|
if (expression.kind == "MethodCallExpression") {
|
|
1134
1140
|
return expressionContainsKind(expression.receiver, kind) ||
|
|
@@ -1936,6 +1942,19 @@ function validateExpression(expression, language, ownerLocation, allowJavaNegate
|
|
|
1936
1942
|
return
|
|
1937
1943
|
}
|
|
1938
1944
|
|
|
1945
|
+
if (candidate.kind == "ConditionalExpression") {
|
|
1946
|
+
const fields = Object.keys(candidate).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
1947
|
+
|
|
1948
|
+
if (fields != "alternate,condition,consequent,kind,location,type") {
|
|
1949
|
+
unsupportedCapability(language, "malformed ConditionalExpression", location)
|
|
1950
|
+
}
|
|
1951
|
+
if (!task048ConditionalLanguages.has(language)) unsupportedCapability(language, "Task 048 conditional expression", location)
|
|
1952
|
+
validateExpression(candidate.condition, language, location, false, expressionPath, externalDeclarationIds)
|
|
1953
|
+
validateExpression(candidate.consequent, language, location, false, expressionPath, externalDeclarationIds)
|
|
1954
|
+
validateExpression(candidate.alternate, language, location, false, expressionPath, externalDeclarationIds)
|
|
1955
|
+
return
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1939
1958
|
unsupportedCapability(language, String(Reflect.get(expression, "kind")), location)
|
|
1940
1959
|
}
|
|
1941
1960
|
|
|
@@ -2364,6 +2383,17 @@ export function emitExpression(writer, expression, path, language, emitIdentifie
|
|
|
2364
2383
|
return
|
|
2365
2384
|
}
|
|
2366
2385
|
|
|
2386
|
+
if (expression.kind == "ConditionalExpression") {
|
|
2387
|
+
writer.mapped("(", {mappingKind: "anchor", node: expression, path})
|
|
2388
|
+
emitExpression(writer, expression.condition, `${path}/condition`, language, emitIdentifier)
|
|
2389
|
+
writer.mapped(" ? ", {mappingKind: "exact", node: expression, path, role: "operator"})
|
|
2390
|
+
emitExpression(writer, expression.consequent, `${path}/consequent`, language, emitIdentifier)
|
|
2391
|
+
writer.mapped(" : ", {mappingKind: "exact", node: expression, path, role: "colon"})
|
|
2392
|
+
emitExpression(writer, expression.alternate, `${path}/alternate`, language, emitIdentifier)
|
|
2393
|
+
writer.mapped(")", {mappingKind: "anchor", node: expression, path})
|
|
2394
|
+
return
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2367
2397
|
if (expression.kind == "UnaryExpression") {
|
|
2368
2398
|
if (language == "dart" && expression.operation == "IntegerNegate") {
|
|
2369
2399
|
emitDartIntegerHelper(writer, expression, path, "_semantifoldIntegerNegate", [
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import path from "node:path"
|
|
4
|
+
import {SemantifoldDiagnostic} from "../diagnostic.js"
|
|
5
|
+
import {
|
|
6
|
+
checkArtifacts,
|
|
7
|
+
checkOutput,
|
|
8
|
+
checkPlan,
|
|
9
|
+
checkStage,
|
|
10
|
+
checkTool,
|
|
11
|
+
environmentPath,
|
|
12
|
+
pathArgument
|
|
13
|
+
} from "./check-plan.js"
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Constructs Swift's warnings-as-errors type-check/compile plan.
|
|
17
|
+
* @param {import("../language-registry.js").TargetCheckPlanContext} context - Validated candidate context.
|
|
18
|
+
* @returns {import("../semantic/types.js").TargetCheckPlan} Immutable Swift plan.
|
|
19
|
+
*/
|
|
20
|
+
export function createSwiftCheckPlan(context) {
|
|
21
|
+
const tool = checkTool(context, "swiftc", "swift")
|
|
22
|
+
const artifacts = checkArtifacts(context, artifact => artifact.path.endsWith(".swift"), "swift", "only staged '.swift' sources")
|
|
23
|
+
|
|
24
|
+
if (artifacts.length != 1 || artifacts[0] != path.join(context.sourcePath, "program.swift")) {
|
|
25
|
+
throw new SemantifoldDiagnostic({code: "INVALID_TARGET_CHECK_PLAN", language: "swift",
|
|
26
|
+
message: "Swift developer checks require the exact producer-owned 'program.swift' source."})
|
|
27
|
+
}
|
|
28
|
+
const home = path.join(context.buildPath, "home")
|
|
29
|
+
const cache = path.join(context.buildPath, "cache")
|
|
30
|
+
const temporary = path.join(context.buildPath, "tmp")
|
|
31
|
+
const environment = {
|
|
32
|
+
HOME: home,
|
|
33
|
+
PATH: [path.dirname(tool.executable), "/usr/bin", "/usr/local/bin", "/bin"].filter((value, index, values) =>
|
|
34
|
+
values.indexOf(value) == index).join(path.delimiter),
|
|
35
|
+
TMPDIR: temporary,
|
|
36
|
+
XDG_CACHE_HOME: cache
|
|
37
|
+
}
|
|
38
|
+
const environmentPaths = [environmentPath("HOME", "build"), environmentPath("TMPDIR", "build"),
|
|
39
|
+
environmentPath("XDG_CACHE_HOME", "build")]
|
|
40
|
+
const objects = path.join(context.buildPath, "objects")
|
|
41
|
+
const object = path.join(objects, "program.o")
|
|
42
|
+
const binary = path.join(context.buildPath, "semantifold-swift")
|
|
43
|
+
const typecheckArgv = [
|
|
44
|
+
"--driver-mode=swiftc", "-warnings-as-errors", "-typecheck", "-module-name", "SemantifoldGenerated", ...artifacts
|
|
45
|
+
]
|
|
46
|
+
const compileArgv = [
|
|
47
|
+
"--driver-mode=swiftc", "-warnings-as-errors", "-c", ...artifacts,
|
|
48
|
+
"-module-name", "SemantifoldGenerated", "-o", "program.o"
|
|
49
|
+
]
|
|
50
|
+
const linkArgv = ["--driver-mode=swiftc", object, "-Xlinker", "--build-id=none", "-o", binary]
|
|
51
|
+
|
|
52
|
+
return checkPlan(context, "swift", artifacts, [
|
|
53
|
+
checkStage({
|
|
54
|
+
argv: typecheckArgv,
|
|
55
|
+
cwd: context.sourcePath,
|
|
56
|
+
environment,
|
|
57
|
+
environmentPaths,
|
|
58
|
+
inputs: artifacts,
|
|
59
|
+
output: null,
|
|
60
|
+
pathArguments: artifacts.map((_, index) => pathArgument(index + 5, "source")),
|
|
61
|
+
stage: "compile",
|
|
62
|
+
tool
|
|
63
|
+
}),
|
|
64
|
+
checkStage({
|
|
65
|
+
argv: compileArgv,
|
|
66
|
+
cwd: objects,
|
|
67
|
+
environment,
|
|
68
|
+
environmentPaths,
|
|
69
|
+
inputs: artifacts,
|
|
70
|
+
output: null,
|
|
71
|
+
pathArguments: artifacts.map((_, index) => pathArgument(index + 3, "source")),
|
|
72
|
+
stage: "compile",
|
|
73
|
+
tool,
|
|
74
|
+
transientPaths: [objects]
|
|
75
|
+
}),
|
|
76
|
+
checkStage({
|
|
77
|
+
argv: linkArgv,
|
|
78
|
+
cwd: context.buildPath,
|
|
79
|
+
environment,
|
|
80
|
+
environmentPaths,
|
|
81
|
+
inputs: [object],
|
|
82
|
+
output: checkOutput(context.buildPath, "application/x-executable", "compiler-output"),
|
|
83
|
+
pathArguments: [pathArgument(1, "build"), pathArgument(linkArgv.length - 1, "build")],
|
|
84
|
+
stage: "compile",
|
|
85
|
+
tool,
|
|
86
|
+
transientPaths: [objects]
|
|
87
|
+
})
|
|
88
|
+
])
|
|
89
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import path from "node:path"
|
|
4
|
+
import {SemantifoldDiagnostic} from "../diagnostic.js"
|
|
5
|
+
import {checkArtifacts, checkPlan, checkStage, checkTool, pathArgument} from "./check-plan.js"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Constructs TypeScript's non-emitting compiler plan.
|
|
9
|
+
* @param {import("../language-registry.js").TargetCheckPlanContext} context - Validated candidate context.
|
|
10
|
+
* @returns {import("../semantic/types.js").TargetCheckPlan} Immutable TypeScript plan.
|
|
11
|
+
*/
|
|
12
|
+
export function createTypeScriptCheckPlan(context) {
|
|
13
|
+
const tool = checkTool(context, "tsc", "typescript")
|
|
14
|
+
const artifacts = checkArtifacts(context, artifact => artifact.path.endsWith(".ts") || path.basename(artifact.path) == "package.json",
|
|
15
|
+
"typescript", "only staged '.ts' artifacts and package manifests")
|
|
16
|
+
const sources = artifacts.filter(artifact => artifact.endsWith(".ts"))
|
|
17
|
+
|
|
18
|
+
if (sources.length == 0) {
|
|
19
|
+
throw new SemantifoldDiagnostic({code: "INVALID_TARGET_CHECK_PLAN", language: "typescript",
|
|
20
|
+
message: "TypeScript developer checks require at least one staged '.ts' artifact."})
|
|
21
|
+
}
|
|
22
|
+
const typeRoots = path.join(context.buildPath, "types")
|
|
23
|
+
const argv = [
|
|
24
|
+
"--pretty", "false", "--noEmit", "--incremental", "false", "--target", "ES2024", "--module", "NodeNext",
|
|
25
|
+
"--moduleResolution", "NodeNext", "--typeRoots", typeRoots, ...sources
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
return checkPlan(context, "typescript", artifacts, [checkStage({
|
|
29
|
+
argv,
|
|
30
|
+
cwd: context.sourcePath,
|
|
31
|
+
inputs: artifacts,
|
|
32
|
+
output: null,
|
|
33
|
+
pathArguments: [pathArgument(12, "build"), ...sources.map((_, index) => pathArgument(13 + index, "source"))],
|
|
34
|
+
stage: "compile",
|
|
35
|
+
tool
|
|
36
|
+
})])
|
|
37
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import path from "node:path"
|
|
4
|
+
import {SemantifoldDiagnostic} from "../diagnostic.js"
|
|
5
|
+
import {
|
|
6
|
+
checkArtifacts,
|
|
7
|
+
checkPlan,
|
|
8
|
+
checkStage,
|
|
9
|
+
checkTool,
|
|
10
|
+
environmentPath,
|
|
11
|
+
pathArgument
|
|
12
|
+
} from "./check-plan.js"
|
|
13
|
+
import {zigBuild} from "./zig-runtime.js"
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Constructs Zig's isolated Debug project build and canonical format-check plan.
|
|
17
|
+
* @param {import("../language-registry.js").TargetCheckPlanContext} context - Validated candidate context.
|
|
18
|
+
* @returns {import("../semantic/types.js").TargetCheckPlan} Immutable Zig plan.
|
|
19
|
+
*/
|
|
20
|
+
export function createZigCheckPlan(context) {
|
|
21
|
+
const tool = checkTool(context, "zig", "zig")
|
|
22
|
+
const artifacts = checkArtifacts(context, artifact => artifact.path == "build.zig" || artifact.path.endsWith(".zig"),
|
|
23
|
+
"zig", "only staged Zig source and the canonical build manifest")
|
|
24
|
+
const manifests = context.artifacts.artifacts.filter(artifact => artifact.path == "build.zig")
|
|
25
|
+
const sources = artifacts.filter(artifact => artifact != path.join(context.sourcePath, "build.zig"))
|
|
26
|
+
|
|
27
|
+
if (manifests.length != 1 || manifests[0].contentKind != "text" || manifests[0].role != "manifest" ||
|
|
28
|
+
manifests[0].content != zigBuild || artifacts.length != 2 || sources.length != 1 ||
|
|
29
|
+
sources[0] != path.join(context.sourcePath, "src/main.zig")) {
|
|
30
|
+
throw new SemantifoldDiagnostic({
|
|
31
|
+
code: "INVALID_TARGET_CHECK_PLAN",
|
|
32
|
+
language: "zig",
|
|
33
|
+
message: "Zig developer checks require the exact standard-library-only 'build.zig' and staged Zig source."
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
const home = path.join(context.buildPath, "home")
|
|
37
|
+
const temporary = path.join(context.buildPath, "tmp")
|
|
38
|
+
const globalCache = path.join(context.buildPath, "zig-global-cache")
|
|
39
|
+
const localCache = path.join(context.buildPath, "zig-local-cache")
|
|
40
|
+
const output = path.join(context.buildPath, "output")
|
|
41
|
+
const environment = {
|
|
42
|
+
HOME: home,
|
|
43
|
+
HTTP_PROXY: "http://127.0.0.1:9",
|
|
44
|
+
HTTPS_PROXY: "http://127.0.0.1:9",
|
|
45
|
+
NO_PROXY: "",
|
|
46
|
+
TMPDIR: temporary,
|
|
47
|
+
ZIG_GLOBAL_CACHE_DIR: globalCache,
|
|
48
|
+
ZIG_LOCAL_CACHE_DIR: localCache
|
|
49
|
+
}
|
|
50
|
+
const environmentPaths = [
|
|
51
|
+
environmentPath("HOME", "build"), environmentPath("TMPDIR", "build"),
|
|
52
|
+
environmentPath("ZIG_GLOBAL_CACHE_DIR", "build"), environmentPath("ZIG_LOCAL_CACHE_DIR", "build")
|
|
53
|
+
]
|
|
54
|
+
const formatArgv = ["fmt", "--check", ...artifacts]
|
|
55
|
+
|
|
56
|
+
return checkPlan(context, "zig", artifacts, [
|
|
57
|
+
checkStage({
|
|
58
|
+
argv: ["build", "-Doptimize=Debug", "--prefix", output],
|
|
59
|
+
cwd: context.sourcePath,
|
|
60
|
+
environment,
|
|
61
|
+
environmentPaths,
|
|
62
|
+
inputs: artifacts,
|
|
63
|
+
output: null,
|
|
64
|
+
pathArguments: [pathArgument(3, "build")],
|
|
65
|
+
stage: "compile",
|
|
66
|
+
tool,
|
|
67
|
+
transientPaths: [output]
|
|
68
|
+
}),
|
|
69
|
+
checkStage({
|
|
70
|
+
argv: formatArgv,
|
|
71
|
+
cwd: context.sourcePath,
|
|
72
|
+
environment,
|
|
73
|
+
environmentPaths,
|
|
74
|
+
inputs: artifacts,
|
|
75
|
+
output: null,
|
|
76
|
+
pathArguments: artifacts.map((_, index) => pathArgument(index + 2, "source")),
|
|
77
|
+
stage: "validate",
|
|
78
|
+
tool
|
|
79
|
+
})
|
|
80
|
+
])
|
|
81
|
+
}
|
package/src/diagnostic.js
CHANGED
|
@@ -19,8 +19,12 @@ export class SemantifoldDiagnostic extends Error {
|
|
|
19
19
|
* @param {string | undefined} [options.stderr] - Captured standard error when available.
|
|
20
20
|
* @param {string | undefined} [options.stdout] - Captured standard output when available.
|
|
21
21
|
* @param {string | undefined} [options.version] - Captured executable version when known.
|
|
22
|
+
* @param {number | undefined} [options.durationMs] - Observed process duration when known.
|
|
23
|
+
* @param {string | undefined} [options.projectId] - Exact project identity when known.
|
|
24
|
+
* @param {string | undefined} [options.targetId] - Exact project target identity when known.
|
|
25
|
+
* @param {string | undefined} [options.toolId] - Exact toolchain identity when known.
|
|
22
26
|
*/
|
|
23
|
-
constructor({cause, code, command, executable, exitCode, generatedLocation, language, location, message, signal, stage, stderr, stdout, version}) {
|
|
27
|
+
constructor({cause, code, command, durationMs, executable, exitCode, generatedLocation, language, location, message, projectId, signal, stage, stderr, stdout, targetId, toolId, version}) {
|
|
24
28
|
const locationText = location ? ` at ${location.filename}:${location.start.line}:${location.start.column}` : ""
|
|
25
29
|
|
|
26
30
|
super(`[${code}] ${language}${locationText}: ${message}`, cause ? {cause} : undefined)
|
|
@@ -31,12 +35,16 @@ export class SemantifoldDiagnostic extends Error {
|
|
|
31
35
|
this.language = language
|
|
32
36
|
this.location = location
|
|
33
37
|
if (command != undefined) this.command = command
|
|
38
|
+
if (durationMs != undefined) this.durationMs = durationMs
|
|
34
39
|
if (executable != undefined) this.executable = executable
|
|
35
40
|
if (exitCode != undefined) this.exitCode = exitCode
|
|
36
41
|
if (signal != undefined) this.signal = signal
|
|
37
42
|
if (stage != undefined) this.stage = stage
|
|
38
43
|
if (stderr != undefined) this.stderr = stderr
|
|
39
44
|
if (stdout != undefined) this.stdout = stdout
|
|
45
|
+
if (projectId != undefined) this.projectId = projectId
|
|
46
|
+
if (targetId != undefined) this.targetId = targetId
|
|
47
|
+
if (toolId != undefined) this.toolId = toolId
|
|
40
48
|
if (version != undefined) this.version = version
|
|
41
49
|
}
|
|
42
50
|
}
|
package/src/frontends/java.js
CHANGED
|
@@ -329,6 +329,29 @@ function convertExpression(node, filename, source, context, expectedType) {
|
|
|
329
329
|
return /** @type {import("../semantic/types.js").Expression} */ (/** @type {unknown} */ (semantic))
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
+
if (node.name == "TernaryExpression") {
|
|
333
|
+
const children = directChildren(node)
|
|
334
|
+
const question = children.find((child) => child.name == "?" || (child.name == "LogicOp" && nodeText(child, source) == "?"))
|
|
335
|
+
const colon = children.find((child) => child.name == ":" || (child.name == "LogicOp" && nodeText(child, source) == ":"))
|
|
336
|
+
const named = children.filter((child) => child !== question && child !== colon)
|
|
337
|
+
|
|
338
|
+
if (!question || !colon || named.length != 3) return unsupportedSyntax("java", "ternary expression", location)
|
|
339
|
+
|
|
340
|
+
return /** @type {import("../semantic/types.js").Expression} */ (/** @type {unknown} */ (withParserRanges({
|
|
341
|
+
alternate: convertExpression(named[2], filename, source, context),
|
|
342
|
+
condition: convertExpression(named[0], filename, source, context),
|
|
343
|
+
consequent: convertExpression(named[1], filename, source, context),
|
|
344
|
+
kind: /** @type {const} */ ("ConditionalExpression"),
|
|
345
|
+
location
|
|
346
|
+
}, {
|
|
347
|
+
alternate: nodeLocation(named[2], filename, source),
|
|
348
|
+
colon: nodeLocation(colon, filename, source),
|
|
349
|
+
condition: nodeLocation(named[0], filename, source),
|
|
350
|
+
consequent: nodeLocation(named[1], filename, source),
|
|
351
|
+
operator: nodeLocation(question, filename, source)
|
|
352
|
+
})))
|
|
353
|
+
}
|
|
354
|
+
|
|
332
355
|
if (node.name == "MethodInvocation") {
|
|
333
356
|
if (isEqualsInvocation(node, source) && !isZeroArgumentRecordMemberInvocation(node, filename, source, context)) {
|
|
334
357
|
const methodName = requiredChild(node, "MethodName", filename, source)
|
|
@@ -626,6 +649,7 @@ function isSupportedExpressionNode(node) {
|
|
|
626
649
|
"StringLiteral",
|
|
627
650
|
"UnaryExpression",
|
|
628
651
|
"BinaryExpression",
|
|
652
|
+
"TernaryExpression",
|
|
629
653
|
"MethodInvocation",
|
|
630
654
|
"ObjectCreationExpression",
|
|
631
655
|
"FieldAccess",
|
|
@@ -7,7 +7,7 @@ import {blockReferencesProtectedEntry} from "../semantic/effects.js"
|
|
|
7
7
|
import {locationFromOffsets, moduleLocation} from "../semantic/location.js"
|
|
8
8
|
import {withAdaptedOperation} from "../semantic/operators.js"
|
|
9
9
|
import {withParserRanges} from "../semantic/provenance.js"
|
|
10
|
-
import {hasOnlyUnicodeScalars} from "../semantic/scalars.js"
|
|
10
|
+
import {hasOnlyUnicodeScalars, scalarType} from "../semantic/scalars.js"
|
|
11
11
|
import {isTask034ResourceProbe} from "../semantic/capabilities.js"
|
|
12
12
|
import {hasExactEffectSupport} from "../backends/effects.js"
|
|
13
13
|
import {requireSourceReturnType, requireSourceScalarType} from "./scalars.js"
|
|
@@ -431,7 +431,38 @@ function convertExpression(node, language, filename, source, context = {bindings
|
|
|
431
431
|
}
|
|
432
432
|
|
|
433
433
|
if (node.type == "TemplateLiteral") {
|
|
434
|
-
if (node.expressions.length > 0)
|
|
434
|
+
if (node.expressions.length > 0) {
|
|
435
|
+
/** @type {import("../semantic/types.js").Expression[]} */
|
|
436
|
+
const ordered = []
|
|
437
|
+
|
|
438
|
+
node.quasis.forEach((quasi, index) => {
|
|
439
|
+
if (typeof quasi.value.cooked != "string" || !hasOnlyUnicodeScalars(quasi.value.cooked)) {
|
|
440
|
+
return unsupportedSyntax(language, "invalid Unicode string literal", nodeLocation(quasi, filename, source))
|
|
441
|
+
}
|
|
442
|
+
const quasiLocation = nodeLocation(quasi, filename, source)
|
|
443
|
+
|
|
444
|
+
if (quasi.value.cooked.length > 0) ordered.push(withParserRanges({kind: /** @type {const} */ ("StringLiteral"), location: quasiLocation, value: quasi.value.cooked}, {literal: quasiLocation}))
|
|
445
|
+
if (index < node.expressions.length) {
|
|
446
|
+
ordered.push(convertExpression(/** @type {import("@babel/types").Expression} */ (node.expressions[index]), language, filename, source, context, scalarType("string")))
|
|
447
|
+
}
|
|
448
|
+
})
|
|
449
|
+
if (ordered.length == 0) return unsupportedSyntax(language, "interpolated string", location)
|
|
450
|
+
|
|
451
|
+
let lowered = ordered[0]
|
|
452
|
+
|
|
453
|
+
for (const part of ordered.slice(1)) {
|
|
454
|
+
const semantic = withAdaptedOperation({
|
|
455
|
+
kind: /** @type {const} */ ("BinaryExpression"),
|
|
456
|
+
left: lowered,
|
|
457
|
+
location,
|
|
458
|
+
right: part
|
|
459
|
+
}, /** @type {import("../semantic/operators.js").AdaptedOperation} */ ("StringConcat"))
|
|
460
|
+
|
|
461
|
+
lowered = /** @type {import("../semantic/types.js").Expression} */ (/** @type {unknown} */ (semantic))
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
return lowered
|
|
465
|
+
}
|
|
435
466
|
|
|
436
467
|
const quasi = node.quasis[0]
|
|
437
468
|
|
|
@@ -442,6 +473,22 @@ function convertExpression(node, language, filename, source, context = {bindings
|
|
|
442
473
|
return withParserRanges({kind: /** @type {const} */ ("StringLiteral"), location, value: quasi.value.cooked}, {literal: location})
|
|
443
474
|
}
|
|
444
475
|
|
|
476
|
+
if (node.type == "ConditionalExpression") {
|
|
477
|
+
return /** @type {import("../semantic/types.js").Expression} */ (/** @type {unknown} */ (withParserRanges({
|
|
478
|
+
alternate: convertExpression(node.alternate, language, filename, source, context),
|
|
479
|
+
condition: convertExpression(node.test, language, filename, source, context),
|
|
480
|
+
consequent: convertExpression(node.consequent, language, filename, source, context),
|
|
481
|
+
kind: /** @type {const} */ ("ConditionalExpression"),
|
|
482
|
+
location
|
|
483
|
+
}, {
|
|
484
|
+
alternate: nodeLocation(node.alternate, filename, source),
|
|
485
|
+
colon: tokenLocation(node, ":", node.consequent.end ?? node.test.end ?? node.start ?? 0, node.alternate.start ?? node.end ?? source.length, filename, source),
|
|
486
|
+
condition: nodeLocation(node.test, filename, source),
|
|
487
|
+
consequent: nodeLocation(node.consequent, filename, source),
|
|
488
|
+
operator: tokenLocation(node, "?", node.test.end ?? node.start ?? 0, node.consequent.start ?? node.end ?? source.length, filename, source)
|
|
489
|
+
})))
|
|
490
|
+
}
|
|
491
|
+
|
|
445
492
|
if (node.type == "UnaryExpression" && ["!", "-"].includes(node.operator)) {
|
|
446
493
|
const operand = convertExpression(node.argument, language, filename, source, context)
|
|
447
494
|
const semantic = withAdaptedOperation(withParserRanges({
|
package/src/frontends/php.js
CHANGED
|
@@ -222,9 +222,37 @@ function convertExpression(node, filename, source, context, expectedType, preser
|
|
|
222
222
|
|
|
223
223
|
if (node.kind == "encapsed") {
|
|
224
224
|
const literal = /** @type {import("php-parser").Encapsed} */ (node)
|
|
225
|
-
const detail = literal.type == "string" ? "interpolated string" : `${literal.type} string`
|
|
226
225
|
|
|
227
|
-
return unsupportedSyntax("php",
|
|
226
|
+
if (literal.type != "string") return unsupportedSyntax("php", `${literal.type} string`, location)
|
|
227
|
+
if (!Array.isArray(literal.value)) return unsupportedSyntax("php", "interpolated string", location)
|
|
228
|
+
|
|
229
|
+
const parts = literal.value.map((part) => {
|
|
230
|
+
if (part.kind != "encapsedpart") return unsupportedSyntax("php", `interpolated string part ${part.kind}`, nodeLocation(part, filename, source))
|
|
231
|
+
const encapsedPart = /** @type {import("php-parser").EncapsedPart} */ (part)
|
|
232
|
+
|
|
233
|
+
if (!encapsedPart.expression) return unsupportedSyntax("php", "interpolated string part without expression", nodeLocation(encapsedPart, filename, source))
|
|
234
|
+
|
|
235
|
+
return convertExpression(encapsedPart.expression, filename, source, context, scalarType("string"))
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
if (parts.length == 0) {
|
|
239
|
+
return withParserRanges({kind: /** @type {const} */ ("StringLiteral"), location, value: ""}, {literal: location})
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
let lowered = parts[0]
|
|
243
|
+
|
|
244
|
+
for (const part of parts.slice(1)) {
|
|
245
|
+
const semantic = withAdaptedOperation({
|
|
246
|
+
kind: /** @type {const} */ ("BinaryExpression"),
|
|
247
|
+
left: lowered,
|
|
248
|
+
location,
|
|
249
|
+
right: part
|
|
250
|
+
}, /** @type {import("../semantic/operators.js").AdaptedOperation} */ ("StringConcat"))
|
|
251
|
+
|
|
252
|
+
lowered = /** @type {import("../semantic/types.js").Expression} */ (/** @type {unknown} */ (semantic))
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return withParserRanges(lowered, {literal: location})
|
|
228
256
|
}
|
|
229
257
|
|
|
230
258
|
if (node.kind == "nowdoc") return unsupportedSyntax("php", "nowdoc string", location)
|
|
@@ -414,6 +442,37 @@ function convertExpression(node, filename, source, context, expectedType, preser
|
|
|
414
442
|
return /** @type {import("../semantic/types.js").Expression} */ (/** @type {unknown} */ (semantic))
|
|
415
443
|
}
|
|
416
444
|
|
|
445
|
+
if (node.kind == "retif") {
|
|
446
|
+
const ternary = /** @type {import("php-parser").RetIf} */ (node)
|
|
447
|
+
|
|
448
|
+
if (!ternary.test || !ternary.trueExpr || !ternary.falseExpr) return unsupportedSyntax("php", "short ternary", location)
|
|
449
|
+
if (ternary.test.kind == "variable") {
|
|
450
|
+
const tested = /** @type {import("php-parser").Variable} */ (ternary.test)
|
|
451
|
+
|
|
452
|
+
if (typeof tested.name == "string" && context.bindings.get(tested.name)?.kind == "OptionalType") {
|
|
453
|
+
return unsupportedSyntax("php", "optional truthiness condition", nodeLocation(tested, filename, source))
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return /** @type {import("../semantic/types.js").Expression} */ (/** @type {unknown} */ (withParserRanges({
|
|
458
|
+
alternate: convertExpression(ternary.falseExpr, filename, source, context),
|
|
459
|
+
condition: convertExpression(ternary.test, filename, source, context),
|
|
460
|
+
consequent: convertExpression(ternary.trueExpr, filename, source, context),
|
|
461
|
+
kind: /** @type {const} */ ("ConditionalExpression"),
|
|
462
|
+
location
|
|
463
|
+
}, {
|
|
464
|
+
alternate: tokenLocation(":", ternary.trueExpr.loc?.end.offset ?? 0, ternary.falseExpr.loc?.start.offset ?? source.length, filename, source),
|
|
465
|
+
colon: tokenLocation(":", ternary.trueExpr.loc?.end.offset ?? 0, ternary.falseExpr.loc?.start.offset ?? source.length, filename, source),
|
|
466
|
+
condition: ternary.test.loc ? nodeLocation(ternary.test, filename, source) : location,
|
|
467
|
+
consequent: ternary.trueExpr.loc ? nodeLocation(ternary.trueExpr, filename, source) : location,
|
|
468
|
+
operator: tokenLocation("?", ternary.test.loc?.end.offset ?? 0, ternary.trueExpr.loc?.start.offset ?? source.length, filename, source)
|
|
469
|
+
})))
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if (node.kind == "nullsafepropertylookup" || node.kind == "nullsafemethodcall") {
|
|
473
|
+
return unsupportedSyntax("php", "safe navigation", location)
|
|
474
|
+
}
|
|
475
|
+
|
|
417
476
|
if (node.kind == "bin") {
|
|
418
477
|
const binary = /** @type {import("php-parser").Bin} */ (node)
|
|
419
478
|
if (binary.type == "!==" && binary.left.kind == "variable" && binary.right.kind == "nullkeyword") {
|
package/src/frontends/program.js
CHANGED
|
@@ -19,6 +19,15 @@ import {inspectRubyModule} from "./ruby.js"
|
|
|
19
19
|
const programLanguages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
20
20
|
const moduleIdPattern = /^[a-z][a-z0-9_]*(?:[.-][a-z][a-z0-9_]*)*$/u
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Reports whether a registered frontend participates in the explicit Task-010 program profile.
|
|
24
|
+
* @param {string} language - Registered source identity.
|
|
25
|
+
* @returns {boolean} Whether the profile accepts the language.
|
|
26
|
+
*/
|
|
27
|
+
export function supportsProgramSourceLanguage(language) {
|
|
28
|
+
return programLanguages.has(/** @type {import("../semantic/types.js").SemanticLanguage} */ (language))
|
|
29
|
+
}
|
|
30
|
+
|
|
22
31
|
/** @typedef {{facade?: import("../stdlib-facades.js").StdlibFacadeRecord, filename: string, id: string, language: import("../semantic/types.js").SemanticLanguage, ownership: "application" | "facade", source: string}} ProgramSource */
|
|
23
32
|
/** @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
33
|
/** @typedef {{importedName: string, localName: string, location: import("../semantic/types.js").SourceLocation, nativeName: string, symbolKind: import("../semantic/types.js").SemanticDeclarationKind, typeOnly: boolean}} ProgramNativeBinding */
|