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.
Files changed (198) hide show
  1. package/README.md +6 -3
  2. package/bin/semantifold.js +6 -0
  3. package/build/bin/semantifold.d.ts +3 -0
  4. package/build/bin/semantifold.d.ts.map +1 -0
  5. package/build/bin/semantifold.js +5 -0
  6. package/build/index.d.ts +8 -0
  7. package/build/index.d.ts.map +1 -1
  8. package/build/index.js +9 -1
  9. package/build/src/artifact-path.d.ts +16 -0
  10. package/build/src/artifact-path.d.ts.map +1 -1
  11. package/build/src/artifact-path.js +29 -3
  12. package/build/src/backends/c-check.d.ts +7 -0
  13. package/build/src/backends/c-check.d.ts.map +1 -0
  14. package/build/src/backends/c-check.js +65 -0
  15. package/build/src/backends/check-plan.d.ts +96 -0
  16. package/build/src/backends/check-plan.d.ts.map +1 -0
  17. package/build/src/backends/check-plan.js +135 -0
  18. package/build/src/backends/cpp-check.d.ts +7 -0
  19. package/build/src/backends/cpp-check.d.ts.map +1 -0
  20. package/build/src/backends/cpp-check.js +59 -0
  21. package/build/src/backends/csharp-check.d.ts +7 -0
  22. package/build/src/backends/csharp-check.d.ts.map +1 -0
  23. package/build/src/backends/csharp-check.js +125 -0
  24. package/build/src/backends/csharp.d.ts +1 -0
  25. package/build/src/backends/csharp.d.ts.map +1 -1
  26. package/build/src/backends/csharp.js +5 -4
  27. package/build/src/backends/dart-check.d.ts +7 -0
  28. package/build/src/backends/dart-check.d.ts.map +1 -0
  29. package/build/src/backends/dart-check.js +103 -0
  30. package/build/src/backends/dart.d.ts +2 -0
  31. package/build/src/backends/dart.d.ts.map +1 -1
  32. package/build/src/backends/dart.js +5 -5
  33. package/build/src/backends/go-check.d.ts +7 -0
  34. package/build/src/backends/go-check.d.ts.map +1 -0
  35. package/build/src/backends/go-check.js +77 -0
  36. package/build/src/backends/go.d.ts +1 -0
  37. package/build/src/backends/go.d.ts.map +1 -1
  38. package/build/src/backends/go.js +3 -3
  39. package/build/src/backends/java-check.d.ts +21 -0
  40. package/build/src/backends/java-check.d.ts.map +1 -0
  41. package/build/src/backends/java-check.js +73 -0
  42. package/build/src/backends/javascript-check.d.ts +7 -0
  43. package/build/src/backends/javascript-check.d.ts.map +1 -0
  44. package/build/src/backends/javascript-check.js +29 -0
  45. package/build/src/backends/kotlin-check.d.ts +7 -0
  46. package/build/src/backends/kotlin-check.d.ts.map +1 -0
  47. package/build/src/backends/kotlin-check.js +31 -0
  48. package/build/src/backends/kotlin.d.ts +1 -1
  49. package/build/src/backends/kotlin.js +2 -2
  50. package/build/src/backends/php-check.d.ts +7 -0
  51. package/build/src/backends/php-check.d.ts.map +1 -0
  52. package/build/src/backends/php-check.js +21 -0
  53. package/build/src/backends/program.d.ts +7 -0
  54. package/build/src/backends/program.d.ts.map +1 -1
  55. package/build/src/backends/program.js +16 -1
  56. package/build/src/backends/python-check.d.ts +7 -0
  57. package/build/src/backends/python-check.d.ts.map +1 -0
  58. package/build/src/backends/python-check.js +28 -0
  59. package/build/src/backends/ruby-check.d.ts +7 -0
  60. package/build/src/backends/ruby-check.d.ts.map +1 -0
  61. package/build/src/backends/ruby-check.js +21 -0
  62. package/build/src/backends/rust-check.d.ts +7 -0
  63. package/build/src/backends/rust-check.d.ts.map +1 -0
  64. package/build/src/backends/rust-check.js +65 -0
  65. package/build/src/backends/shared.d.ts.map +1 -1
  66. package/build/src/backends/shared.js +29 -1
  67. package/build/src/backends/swift-check.d.ts +7 -0
  68. package/build/src/backends/swift-check.d.ts.map +1 -0
  69. package/build/src/backends/swift-check.js +77 -0
  70. package/build/src/backends/typescript-check.d.ts +7 -0
  71. package/build/src/backends/typescript-check.d.ts.map +1 -0
  72. package/build/src/backends/typescript-check.js +33 -0
  73. package/build/src/backends/zig-check.d.ts +7 -0
  74. package/build/src/backends/zig-check.d.ts.map +1 -0
  75. package/build/src/backends/zig-check.js +70 -0
  76. package/build/src/diagnostic.d.ts +13 -1
  77. package/build/src/diagnostic.d.ts.map +1 -1
  78. package/build/src/diagnostic.js +14 -2
  79. package/build/src/frontends/java.d.ts.map +1 -1
  80. package/build/src/frontends/java.js +23 -1
  81. package/build/src/frontends/javascript-typescript.d.ts.map +1 -1
  82. package/build/src/frontends/javascript-typescript.js +45 -4
  83. package/build/src/frontends/php.d.ts.map +1 -1
  84. package/build/src/frontends/php.js +54 -3
  85. package/build/src/frontends/program.d.ts +6 -0
  86. package/build/src/frontends/program.d.ts.map +1 -1
  87. package/build/src/frontends/program.js +9 -1
  88. package/build/src/frontends/ruby.d.ts.map +1 -1
  89. package/build/src/frontends/ruby.js +55 -5
  90. package/build/src/language-registry.d.ts +54 -0
  91. package/build/src/language-registry.d.ts.map +1 -1
  92. package/build/src/language-registry.js +124 -10
  93. package/build/src/project-build.d.ts +172 -0
  94. package/build/src/project-build.d.ts.map +1 -0
  95. package/build/src/project-build.js +320 -0
  96. package/build/src/project-cli.d.ts +40 -0
  97. package/build/src/project-cli.d.ts.map +1 -0
  98. package/build/src/project-cli.js +145 -0
  99. package/build/src/project-manifest.d.ts +148 -0
  100. package/build/src/project-manifest.d.ts.map +1 -0
  101. package/build/src/project-manifest.js +432 -0
  102. package/build/src/project-reporter.d.ts +81 -0
  103. package/build/src/project-reporter.d.ts.map +1 -0
  104. package/build/src/project-reporter.js +293 -0
  105. package/build/src/project-snapshot.d.ts +129 -0
  106. package/build/src/project-snapshot.d.ts.map +1 -0
  107. package/build/src/project-snapshot.js +362 -0
  108. package/build/src/project-watch-reporter.d.ts +125 -0
  109. package/build/src/project-watch-reporter.d.ts.map +1 -0
  110. package/build/src/project-watch-reporter.js +280 -0
  111. package/build/src/project-watch.d.ts +133 -0
  112. package/build/src/project-watch.d.ts.map +1 -0
  113. package/build/src/project-watch.js +839 -0
  114. package/build/src/publication.d.ts +69 -5
  115. package/build/src/publication.d.ts.map +1 -1
  116. package/build/src/publication.js +246 -57
  117. package/build/src/semantic/effects.js +3 -1
  118. package/build/src/semantic/provenance.d.ts.map +1 -1
  119. package/build/src/semantic/provenance.js +11 -1
  120. package/build/src/semantic/types.d.ts +355 -4
  121. package/build/src/semantic/types.d.ts.map +1 -1
  122. package/build/src/semantic/types.js +92 -3
  123. package/build/src/semantic/validate.js +18 -1
  124. package/build/src/subprocess.d.ts +8 -0
  125. package/build/src/subprocess.d.ts.map +1 -1
  126. package/build/src/subprocess.js +50 -9
  127. package/build/src/target-check.d.ts +52 -0
  128. package/build/src/target-check.d.ts.map +1 -0
  129. package/build/src/target-check.js +796 -0
  130. package/build/src/toolchains.d.ts.map +1 -1
  131. package/build/src/toolchains.js +3 -3
  132. package/docs/adr-expression-breadth.md +186 -0
  133. package/docs/architecture.md +3 -1
  134. package/docs/c.md +4 -0
  135. package/docs/cpp.md +4 -0
  136. package/docs/dart.md +4 -0
  137. package/docs/generated-artifact-publication.md +11 -8
  138. package/docs/kotlin.md +2 -2
  139. package/docs/language-support.md +40 -6
  140. package/docs/parser-qualification.md +1 -1
  141. package/docs/project-build-cli.md +147 -0
  142. package/docs/project-watch.md +60 -0
  143. package/docs/rust.md +4 -0
  144. package/docs/swift.md +2 -0
  145. package/docs/testing.md +70 -3
  146. package/docs/watch-build-pipeline.md +39 -23
  147. package/docs/zig.md +2 -0
  148. package/examples/jsdoc-java-watch/expected-output.txt +3 -0
  149. package/examples/jsdoc-java-watch/semantifold.json +23 -0
  150. package/examples/jsdoc-java-watch/src/main.js +21 -0
  151. package/index.js +8 -0
  152. package/package.json +6 -1
  153. package/src/artifact-path.js +31 -2
  154. package/src/backends/c-check.js +80 -0
  155. package/src/backends/check-plan.js +147 -0
  156. package/src/backends/cpp-check.js +73 -0
  157. package/src/backends/csharp-check.js +139 -0
  158. package/src/backends/csharp.js +4 -3
  159. package/src/backends/dart-check.js +117 -0
  160. package/src/backends/dart.js +4 -4
  161. package/src/backends/go-check.js +89 -0
  162. package/src/backends/go.js +2 -2
  163. package/src/backends/java-check.js +77 -0
  164. package/src/backends/javascript-check.js +33 -0
  165. package/src/backends/kotlin-check.js +41 -0
  166. package/src/backends/kotlin.js +1 -1
  167. package/src/backends/php-check.js +23 -0
  168. package/src/backends/program.js +17 -0
  169. package/src/backends/python-check.js +30 -0
  170. package/src/backends/ruby-check.js +23 -0
  171. package/src/backends/rust-check.js +78 -0
  172. package/src/backends/shared.js +30 -0
  173. package/src/backends/swift-check.js +89 -0
  174. package/src/backends/typescript-check.js +37 -0
  175. package/src/backends/zig-check.js +81 -0
  176. package/src/diagnostic.js +9 -1
  177. package/src/frontends/java.js +24 -0
  178. package/src/frontends/javascript-typescript.js +49 -2
  179. package/src/frontends/php.js +61 -2
  180. package/src/frontends/program.js +9 -0
  181. package/src/frontends/ruby.js +62 -2
  182. package/src/language-registry.js +130 -9
  183. package/src/project-build.js +353 -0
  184. package/src/project-cli.js +155 -0
  185. package/src/project-manifest.js +479 -0
  186. package/src/project-reporter.js +320 -0
  187. package/src/project-snapshot.js +403 -0
  188. package/src/project-watch-reporter.js +303 -0
  189. package/src/project-watch.js +870 -0
  190. package/src/publication.js +275 -58
  191. package/src/semantic/effects.js +1 -0
  192. package/src/semantic/provenance.js +8 -0
  193. package/src/semantic/types.js +101 -2
  194. package/src/semantic/validate.js +26 -0
  195. package/src/subprocess.js +49 -8
  196. package/src/target-check.js +844 -0
  197. package/src/toolchains.js +3 -2
  198. 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
+ }
@@ -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
  }
@@ -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) return unsupportedSyntax(language, "interpolated string", location)
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({
@@ -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", detail, location)
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") {
@@ -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 */