semantifold 0.10.0 → 0.12.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 +2 -2
- package/build/src/backends/c.d.ts +2 -1
- package/build/src/backends/c.d.ts.map +1 -1
- package/build/src/backends/c.js +26 -5
- package/build/src/backends/cpp.js +3 -1
- package/build/src/backends/kotlin.d.ts +1 -1
- package/build/src/backends/kotlin.js +2 -2
- package/build/src/backends/ordered-expressions.d.ts +19 -7
- package/build/src/backends/ordered-expressions.d.ts.map +1 -1
- package/build/src/backends/ordered-expressions.js +46 -11
- package/build/src/backends/shared.d.ts.map +1 -1
- package/build/src/backends/shared.js +30 -2
- package/build/src/frontends/c.d.ts +34 -1
- package/build/src/frontends/c.d.ts.map +1 -1
- package/build/src/frontends/c.js +128 -21
- 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/ruby.d.ts.map +1 -1
- package/build/src/frontends/ruby.js +55 -5
- package/build/src/language-registry.d.ts.map +1 -1
- package/build/src/language-registry.js +33 -6
- 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 +52 -2
- package/build/src/semantic/types.d.ts.map +1 -1
- package/build/src/semantic/types.js +14 -2
- package/build/src/semantic/validate.js +19 -2
- package/build/src/toolchains.js +3 -3
- package/docs/adr-expression-breadth.md +186 -0
- package/docs/c.md +1 -1
- package/docs/kotlin.md +2 -2
- package/docs/language-support.md +14 -5
- package/docs/parser-qualification.md +1 -1
- package/docs/testing.md +5 -4
- package/package.json +1 -1
- package/src/backends/c.js +23 -4
- package/src/backends/cpp.js +1 -0
- package/src/backends/kotlin.js +1 -1
- package/src/backends/ordered-expressions.js +46 -10
- package/src/backends/shared.js +31 -1
- package/src/frontends/c.js +135 -19
- 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/ruby.js +62 -2
- package/src/language-registry.js +32 -5
- package/src/semantic/effects.js +1 -0
- package/src/semantic/provenance.js +8 -0
- package/src/semantic/types.js +14 -1
- package/src/semantic/validate.js +27 -1
- package/src/toolchains.js +2 -2
package/docs/testing.md
CHANGED
|
@@ -36,7 +36,7 @@ These tests generate and publish source artifacts only. They do not run `javac`,
|
|
|
36
36
|
|
|
37
37
|
## Task 005 general function signatures and calls
|
|
38
38
|
|
|
39
|
-
Task 005 stays bounded to PHP, Ruby, JavaScript with JSDoc, TypeScript, and
|
|
39
|
+
Task 005 stays bounded to PHP, Ruby, JavaScript with JSDoc, TypeScript, Java, Dart, Zig, and C. `general-function-signatures-and-calls.spec.js` proves equivalent zero-, one-, and three-parameter signatures, complete signature collection, nested calls, terminating recursion, scalar/void returns, and the stable semantic failures. `general-function-frontend-validation.spec.js` owns the excluded parameter, call, method, receiver, dispatch, annotation, overload, generic, and higher-order source forms. `general-function-backend-validation.spec.js` proves generation/reparse, exact external-IR signature revalidation, target conflicts, the public feature flag, and transactional rejection from every deferred Task 025/Wasm target. `general-function-provenance-and-mapping.spec.js` proves deterministic declaration/resolution identities and exact signature/call mappings. `general-function-runtime-execution.spec.js` invokes real PHP, Ruby, Node, local `tsc` followed by Node, `javac`, and `java`, requiring exact `ready\n6\n` output; a missing command fails. `c-task005-acceptance.spec.js` proves the C cohort extension: generated-C reparse with identical meaning, the C fixture through every original-five text target, and real Clang execution of each original-five Task-005 fixture and the C fixture at `-O0`/`-O2` with and without sanitizers, including prototype-mediated mutual recursion and bare void returns.
|
|
40
40
|
|
|
41
41
|
Run the Task 005 files individually and sequentially:
|
|
42
42
|
|
|
@@ -46,9 +46,10 @@ npx velocious-test spec/general-function-frontend-validation.spec.js
|
|
|
46
46
|
npx velocious-test spec/general-function-backend-validation.spec.js
|
|
47
47
|
npx velocious-test spec/general-function-provenance-and-mapping.spec.js
|
|
48
48
|
npx velocious-test spec/general-function-runtime-execution.spec.js
|
|
49
|
+
npx velocious-test spec/c-task005-acceptance.spec.js
|
|
49
50
|
```
|
|
50
51
|
|
|
51
|
-
Task 013 is the separate terminal original-five aggregate gate. Task 005 does not expand that matrix or adopt Python, C#, C
|
|
52
|
+
Task 013 is the separate terminal original-five aggregate gate. Task 005 does not expand that matrix or adopt Python, C#, C++, Rust, Swift, Kotlin/JVM, Go, or browser Wasm beyond focused capability rejection.
|
|
52
53
|
|
|
53
54
|
## Task 011 typed errors and handling
|
|
54
55
|
|
|
@@ -380,7 +381,7 @@ Task 017 adds the distinct ordered `restore` stage and configured .NET SDK 10 di
|
|
|
380
381
|
|
|
381
382
|
Task 024 adds configured Go 1.26.x discovery restricted to Linux/amd64; the patch release is read from the discovered toolchain rather than fixed by runtime tests. Acceptance obtains `GOROOT` from that exact `go`, requires the companion `gofmt` beside the configured or discovered executable to resolve to the same `GOROOT/bin/gofmt`, and checks `gofmt -d` before native execution. Each dependency-free generated module is run with `GOTOOLCHAIN=local`, `GOPROXY=off`, `GOSUMDB=off`, `GOVCS=off`, `CGO_ENABLED=0`, `GOENV=off`, `GOWORK=off`, `GOOS=linux`, `GOARCH=amd64`, and isolated HOME/GOPATH/build/module caches. The short-lived discovery fixture also writes exact `off` mode into its isolated Go telemetry directory before invoking the tool, then asserts that the mode file is the only telemetry state: Go 1.26 otherwise starts an unawaited uploader sidecar even in default `local` mode, which can recreate `upload/` while the fixture root is being removed. Cleanup errors remain fatal. Stages run offline `go build`, mandatory `go vet`, and `go run`, while compiler-only differential cases use the matching `go tool compile`. Tests require exact status/stdout, unchanged `go.mod`/`main.go`, no `go.sum`/`go.work`/vendor tree, and byte-identical binaries and output in two fresh directories. Missing, wrong-minor, wrong-architecture, mismatched-gofmt, download, cgo, workspace, vet, formatting, nondeterminism, telemetry leakage, or cleanup evidence fails without a skip.
|
|
382
383
|
|
|
383
|
-
Task 023 adds exact Kotlin/JVM 2.4.20 discovery on pinned OpenJDK 25 Ubuntu runtimes. Ubuntu26.04 development uses exact `25.0.4.1+1-1~26.04.4`; Ubuntu24.04 TensorBuzz
|
|
384
|
+
Task 023 adds exact Kotlin/JVM 2.4.20 discovery on pinned OpenJDK 25 Ubuntu runtimes. Ubuntu26.04 development uses exact `25.0.4.1+1-1~26.04.4`; Ubuntu24.04 TensorBuzz uses exact `25.0.4.1+1-1~24.04.4`. Bootstrap and discovery accept only those two qualified compiler-reported package identities, never arbitrary output. Original and generated `Program.kt` files compile with `-language-version 2.4 -api-version 2.4 -jvm-target 25 -Werror -include-runtime` into runnable `Program.jar` files, then execute through the exact `java25` discovery contract. Every process uses a fresh directory and exact status/stderr/UTF-8 stdout assertions; no test downloads a compiler or dependency.
|
|
384
385
|
|
|
385
386
|
Task 041's product checks are separate from runtime acceptance. Run the two focused files sequentially:
|
|
386
387
|
|
|
@@ -424,7 +425,7 @@ No Task 042 plan runs a generated executable, fetches a package, consults a user
|
|
|
424
425
|
| `tsc` | `tsc` | `SEMANTIFOLD_TSC` | TypeScript 7.x |
|
|
425
426
|
| `javac` | `javac` | `SEMANTIFOLD_JAVAC` | JDK 17–29 |
|
|
426
427
|
| `java` | `java` | `SEMANTIFOLD_JAVA` | JRE 17–29 |
|
|
427
|
-
| `java25` | `java` | `SEMANTIFOLD_JAVA` | exact OpenJDK 25.0.4.1+1 development or
|
|
428
|
+
| `java25` | `java` | `SEMANTIFOLD_JAVA` | exact OpenJDK 25.0.4.1+1 development or TensorBuzz runtime for JVM25 artifacts |
|
|
428
429
|
| `kotlinc` | `kotlinc` | `SEMANTIFOLD_KOTLINC` | exact Kotlin/JVM 2.4.20 on the matching qualified OpenJDK 25 Ubuntu package |
|
|
429
430
|
| `python` | `python3` | `SEMANTIFOLD_PYTHON` | Python 3.x |
|
|
430
431
|
| `dotnet` | `dotnet` | `SEMANTIFOLD_DOTNET` | .NET SDK 10.x |
|
package/package.json
CHANGED
package/src/backends/c.js
CHANGED
|
@@ -16,7 +16,7 @@ import {SourceWriter} from "./writer.js"
|
|
|
16
16
|
/** @typedef {import("../semantic/types.js").Block} Block */
|
|
17
17
|
/** @typedef {import("./ordered-expressions.js").PlannedValue} PlannedValue */
|
|
18
18
|
/** @typedef {import("./ordered-expressions.js").PlannedStep} PlannedStep */
|
|
19
|
-
/** @typedef {{id: string, steps: PlannedStep[], value
|
|
19
|
+
/** @typedef {{id: string, steps: PlannedStep[], value?: PlannedValue, terminal?: PlannedStep}} StatementPlan */
|
|
20
20
|
|
|
21
21
|
const binaryTokens = new Map([
|
|
22
22
|
["IntegerAdd", "semantifold_integer_add"], ["IntegerSubtract", "semantifold_integer_subtract"],
|
|
@@ -125,7 +125,7 @@ class CEmitter {
|
|
|
125
125
|
module.functions.forEach((declaration, index) => {
|
|
126
126
|
const parameters = declaration.parameters.map((parameter) => `${emitScalarType("c", parameter.type)} ${parameter.name}`).join(", ")
|
|
127
127
|
|
|
128
|
-
this.synthetic(`static ${emitScalarType("c", declaration.returnType)} ${declaration.name}(${parameters});\n`, declaration, `/functions/${index}`)
|
|
128
|
+
this.synthetic(`static ${emitScalarType("c", declaration.returnType)} ${declaration.name}(${declaration.parameters.length ? parameters : "void"});\n`, declaration, `/functions/${index}`)
|
|
129
129
|
})
|
|
130
130
|
module.functions.forEach((declaration, index) => {
|
|
131
131
|
const path = `/functions/${index}`
|
|
@@ -135,6 +135,7 @@ class CEmitter {
|
|
|
135
135
|
this.synthetic(" ", declaration, path)
|
|
136
136
|
this.mapped(declaration.name, declaration, path, "name")
|
|
137
137
|
this.synthetic("(", declaration, path)
|
|
138
|
+
if (!declaration.parameters.length) this.synthetic("void", declaration, path)
|
|
138
139
|
declaration.parameters.forEach((parameter, parameterIndex) => {
|
|
139
140
|
const parameterPath = `${path}/parameters/${parameterIndex}`
|
|
140
141
|
|
|
@@ -181,7 +182,25 @@ class CEmitter {
|
|
|
181
182
|
|
|
182
183
|
this.synthetic(`${indent}/* ${marker} begin ${plan.id} ${signature} */\n`, statement, path)
|
|
183
184
|
this.steps(plan.steps, indent)
|
|
185
|
+
if (statement.kind == "ReturnStatement" && !statement.expression) {
|
|
186
|
+
this.synthetic(indent, statement, path)
|
|
187
|
+
this.mapped("return", statement, path)
|
|
188
|
+
this.synthetic(";\n", statement, path)
|
|
189
|
+
this.synthetic(`${indent}/* ${marker} end ${plan.id} ${signature} */\n`, statement, path)
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
if (statement.kind == "ExpressionStatement") {
|
|
193
|
+
if (!plan.terminal) throw new Error("Missing validated C terminal call.")
|
|
194
|
+
this.synthetic(indent, statement, path)
|
|
195
|
+
this.operation(plan.terminal)
|
|
196
|
+
this.synthetic(";\n", statement, path)
|
|
197
|
+
this.synthetic(`${indent}/* ${marker} end ${plan.id} ${signature} */\n`, statement, path)
|
|
198
|
+
return
|
|
199
|
+
}
|
|
184
200
|
this.synthetic(indent, statement, path)
|
|
201
|
+
const value = plan.value
|
|
202
|
+
|
|
203
|
+
if (!value) throw new Error("Missing validated C statement value plan.")
|
|
185
204
|
if (statement.kind == "LocalDeclaration") {
|
|
186
205
|
if (!statement.mutable) this.synthetic("const ", statement, path)
|
|
187
206
|
this.mapped(emitScalarType("c", statement.type), statement.type, `${path}/type`, "type")
|
|
@@ -196,9 +215,9 @@ class CEmitter {
|
|
|
196
215
|
this.mapped("=", statement, path, "operator")
|
|
197
216
|
this.synthetic(" ", statement, path)
|
|
198
217
|
} else if (statement.kind == "ReturnStatement") this.mapped("return ", statement, path)
|
|
199
|
-
else if (statement.kind == "PrintStatement") this.mapped(`semantifold_print_${
|
|
218
|
+
else if (statement.kind == "PrintStatement") this.mapped(`semantifold_print_${value.type}(`, statement, path)
|
|
200
219
|
else this.mapped("if (", statement, path)
|
|
201
|
-
this.value(
|
|
220
|
+
this.value(value)
|
|
202
221
|
if (statement.kind == "IfStatement") {
|
|
203
222
|
this.synthetic(") {\n", statement, path)
|
|
204
223
|
this.block(statement.consequent, `${path}/consequent`, `${indent} `)
|
package/src/backends/cpp.js
CHANGED
|
@@ -153,6 +153,7 @@ class CppEmitter {
|
|
|
153
153
|
const plan = this.plans.get(path)
|
|
154
154
|
|
|
155
155
|
if (!plan) throw new Error("Missing validated C++ statement plan.")
|
|
156
|
+
if (!plan.value) throw new Error("Missing validated C++ statement value plan.")
|
|
156
157
|
const marker = `semantifold:ordered-expression:cpp:v1`
|
|
157
158
|
const signature = statementSignature(statement)
|
|
158
159
|
|
package/src/backends/kotlin.js
CHANGED
|
@@ -15,7 +15,7 @@ export const kotlinArtifactMetadata = Object.freeze({
|
|
|
15
15
|
toolchain: "kotlinc",
|
|
16
16
|
version: "2.4.20"
|
|
17
17
|
}),
|
|
18
|
-
run: Object.freeze({arguments: Object.freeze(["-jar", "Program.jar"]), toolchain: "java25", version: "25.0.4
|
|
18
|
+
run: Object.freeze({arguments: Object.freeze(["-jar", "Program.jar"]), toolchain: "java25", version: "25.0.4.1"})
|
|
19
19
|
})
|
|
20
20
|
|
|
21
21
|
/**
|
|
@@ -5,7 +5,8 @@ import {unsupportedCapability} from "../diagnostic.js"
|
|
|
5
5
|
|
|
6
6
|
/** @typedef {import("../semantic/types.js").Expression} Expression */
|
|
7
7
|
/** @typedef {import("../semantic/types.js").SemanticTypeName} Scalar */
|
|
8
|
-
/** @typedef {
|
|
8
|
+
/** @typedef {Scalar | "void"} PlannedType */
|
|
9
|
+
/** @typedef {{expression: Expression, path: string, type: PlannedType, name?: string}} PlannedValue */
|
|
9
10
|
/** @typedef {{result: PlannedValue, operands: PlannedValue[], rightSteps?: PlannedStep[]}} PlannedStep */
|
|
10
11
|
/** @typedef {Array<string | number | boolean | Signature>} Signature */
|
|
11
12
|
|
|
@@ -20,7 +21,7 @@ export class OrderedExpressionPlanner {
|
|
|
20
21
|
this.language = language
|
|
21
22
|
this.functions = new Map(module.functions.map((declaration) => [
|
|
22
23
|
declaration.name,
|
|
23
|
-
|
|
24
|
+
returnName(declaration.returnType, language, declaration.location)
|
|
24
25
|
]))
|
|
25
26
|
this.nextTemporary = 1
|
|
26
27
|
}
|
|
@@ -29,7 +30,7 @@ export class OrderedExpressionPlanner {
|
|
|
29
30
|
* Resolves an expression type from the validated lexical environment.
|
|
30
31
|
* @param {Expression} expression - Semantic expression.
|
|
31
32
|
* @param {Map<string, Scalar>} bindings - Lexical scalar types.
|
|
32
|
-
* @returns {
|
|
33
|
+
* @returns {PlannedType} Exact semantic result type.
|
|
33
34
|
*/
|
|
34
35
|
type(expression, bindings) {
|
|
35
36
|
if (expression.kind == "IntegerLiteral") return "integer"
|
|
@@ -76,12 +77,31 @@ export class OrderedExpressionPlanner {
|
|
|
76
77
|
}
|
|
77
78
|
operands.push(this.plan(expression.right, `${path}/right`, bindings, steps))
|
|
78
79
|
}
|
|
80
|
+
if (result.type == "void") throw new Error("Void call reached native value planning.")
|
|
79
81
|
const planned = {...result, name: this.allocate(expression)}
|
|
80
82
|
|
|
81
83
|
steps.push({operands, result: planned})
|
|
82
84
|
return planned
|
|
83
85
|
}
|
|
84
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Plans the arguments of one terminal void call without inventing a void temporary.
|
|
89
|
+
* @param {Expression} expression - Terminal expression occurrence.
|
|
90
|
+
* @param {string} path - Occurrence path.
|
|
91
|
+
* @param {Map<string, Scalar>} bindings - Visible scalar bindings.
|
|
92
|
+
* @param {PlannedStep[]} steps - Ordered enclosing prelude.
|
|
93
|
+
* @returns {PlannedStep} Terminal direct-call step.
|
|
94
|
+
*/
|
|
95
|
+
terminal(expression, path, bindings, steps) {
|
|
96
|
+
if (expression.kind != "CallExpression" || this.type(expression, bindings) != "void") {
|
|
97
|
+
throw new Error("Non-void expression reached native terminal-call planning.")
|
|
98
|
+
}
|
|
99
|
+
const result = {expression, path, type: /** @type {const} */ ("void")}
|
|
100
|
+
const operands = expression.arguments.map((argument, index) => this.plan(argument, `${path}/arguments/${index}`, bindings, steps))
|
|
101
|
+
|
|
102
|
+
return {operands, result}
|
|
103
|
+
}
|
|
104
|
+
|
|
85
105
|
/**
|
|
86
106
|
* Allocates one bounded deterministic occurrence name.
|
|
87
107
|
* @param {Expression} expression - Related expression.
|
|
@@ -108,9 +128,9 @@ export function statementSignature(statement) {
|
|
|
108
128
|
statement.kind == "AssignmentStatement" || statement.kind == "ExpressionStatement" || statement.kind == "PrintStatement" ? statement.expression :
|
|
109
129
|
statement.kind == "ReturnStatement" ? statement.expression : undefined
|
|
110
130
|
|
|
111
|
-
if (!expression) throw new TypeError("Bare return reached native statement signature planning.")
|
|
131
|
+
if (!expression && statement.kind != "ReturnStatement") throw new TypeError("Bare return reached native statement signature planning.")
|
|
112
132
|
|
|
113
|
-
return createHash("sha256").update(JSON.stringify([consumer, expressionSignature(expression)])).digest("hex")
|
|
133
|
+
return createHash("sha256").update(JSON.stringify([consumer, expression ? expressionSignature(expression) : null])).digest("hex")
|
|
114
134
|
}
|
|
115
135
|
|
|
116
136
|
/**
|
|
@@ -128,7 +148,7 @@ function expressionSignature(expression) {
|
|
|
128
148
|
return [expression.kind, expression.operation, expression.type, expressionSignature(expression.left), expressionSignature(expression.right)]
|
|
129
149
|
}
|
|
130
150
|
|
|
131
|
-
/** @typedef {{id: string, steps: PlannedStep[], value
|
|
151
|
+
/** @typedef {{id: string, steps: PlannedStep[], value?: PlannedValue, terminal?: PlannedStep}} StatementPlan */
|
|
132
152
|
/**
|
|
133
153
|
* Plans every occurrence before constructing a writer or exposing either artifact.
|
|
134
154
|
* @param {import("../semantic/types.js").SemanticModule} module - Validated module.
|
|
@@ -159,12 +179,13 @@ export function planNativeModule(module, language = "c") {
|
|
|
159
179
|
statement.kind == "ReturnStatement" ? statement.expression : undefined
|
|
160
180
|
/** @type {PlannedStep[]} */
|
|
161
181
|
const steps = []
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const value = planner.plan(expression, `${statementPath}/${field}`, bindings, steps)
|
|
182
|
+
/** @type {StatementPlan} */
|
|
183
|
+
const plan = {id: String(plans.size + 1).padStart(6, "0"), steps}
|
|
165
184
|
|
|
166
185
|
if (plans.size >= 999999) unsupportedCapability(language, "ordered statement limit", statement.location)
|
|
167
|
-
|
|
186
|
+
if (statement.kind == "ExpressionStatement") plan.terminal = planner.terminal(statement.expression, `${statementPath}/${field}`, bindings, steps)
|
|
187
|
+
else if (expression) plan.value = planner.plan(expression, `${statementPath}/${field}`, bindings, steps)
|
|
188
|
+
plans.set(statementPath, plan)
|
|
168
189
|
if (statement.kind == "LocalDeclaration") bindings.set(statement.name, scalarName(statement.type, language, statement.location))
|
|
169
190
|
if (statement.kind == "IfStatement") {
|
|
170
191
|
visit(statement.consequent, `${statementPath}/consequent`, bindings)
|
|
@@ -193,3 +214,18 @@ function scalarName(type, language, location) {
|
|
|
193
214
|
|
|
194
215
|
return type.name
|
|
195
216
|
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Narrows a validated function return, admitting the non-value `void` result.
|
|
220
|
+
* @param {import("../semantic/types.js").SemanticFunctionReturnType} type - Semantic type.
|
|
221
|
+
* @param {"c" | "cpp"} language - Diagnostic language.
|
|
222
|
+
* @param {import("../semantic/types.js").SourceLocation} location - Owning location.
|
|
223
|
+
* @returns {PlannedType} Scalar or void name.
|
|
224
|
+
*/
|
|
225
|
+
function returnName(type, language, location) {
|
|
226
|
+
if (type.kind != "TypeReference") {
|
|
227
|
+
return unsupportedCapability(language, "collection type reached native scalar planning", location)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return type.name
|
|
231
|
+
}
|
package/src/backends/shared.js
CHANGED
|
@@ -27,7 +27,7 @@ const binaryOperationSyntax = Object.freeze({
|
|
|
27
27
|
StringEqual: Object.freeze({default: "==", strict: "==="}),
|
|
28
28
|
StringNotEqual: Object.freeze({default: "!=", strict: "!=="})
|
|
29
29
|
})
|
|
30
|
-
const task005Languages = new Set(["php", "ruby", "javascript", "typescript", "java", "dart", "zig"])
|
|
30
|
+
const task005Languages = new Set(["php", "ruby", "javascript", "typescript", "java", "c", "dart", "zig"])
|
|
31
31
|
const task006Languages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
32
32
|
const task007Languages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
33
33
|
const task008Languages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
@@ -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", [
|
package/src/frontends/c.js
CHANGED
|
@@ -12,7 +12,7 @@ import {withAdaptedOperation} from "../semantic/operators.js"
|
|
|
12
12
|
import {withParserRanges} from "../semantic/provenance.js"
|
|
13
13
|
import {hasOnlyUnicodeScalars} from "../semantic/scalars.js"
|
|
14
14
|
import {validateParsedModule} from "../semantic/validate.js"
|
|
15
|
-
import {sourceScalarType} from "./scalars.js"
|
|
15
|
+
import {sourceScalarType, requireSourceReturnType} from "./scalars.js"
|
|
16
16
|
|
|
17
17
|
/** @typedef {import("./c-parser.js").CstNode} CstNode */
|
|
18
18
|
/** @typedef {import("../semantic/types.js").Expression} Expression */
|
|
@@ -29,6 +29,15 @@ const helpers = new Map([
|
|
|
29
29
|
["semantifold_string_equal", "StringEqual"], ["semantifold_string_not_equal", "StringNotEqual"]
|
|
30
30
|
])
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Reads the exact scalar-or-void type name of a C declaration annotation.
|
|
34
|
+
* @param {import("../semantic/types.js").SemanticFunctionReturnType | import("../semantic/types.js").SemanticValueType} type - Converted C type.
|
|
35
|
+
* @returns {string} Type name.
|
|
36
|
+
*/
|
|
37
|
+
function returnTypeName(type) {
|
|
38
|
+
return type.kind == "TypeReference" ? type.name : type.kind
|
|
39
|
+
}
|
|
40
|
+
|
|
32
41
|
/** Strict field-and-token consumer for the private frozen C CST. */
|
|
33
42
|
export class CReader {
|
|
34
43
|
/**
|
|
@@ -48,7 +57,11 @@ export class CReader {
|
|
|
48
57
|
/** @type {Map<Statement, {node: CstNode, type: string}>} */
|
|
49
58
|
this.printTypes = new Map()
|
|
50
59
|
/** @type {Set<string>} */
|
|
51
|
-
this.
|
|
60
|
+
this.declaredFunctionNames = new Set()
|
|
61
|
+
/** @type {{name: string, node: CstNode, parameterTypes: string[], returnType: string}[]} */
|
|
62
|
+
this.prototypes = []
|
|
63
|
+
/** @type {boolean} */
|
|
64
|
+
this.voidFunction = false
|
|
52
65
|
}
|
|
53
66
|
|
|
54
67
|
/**
|
|
@@ -303,10 +316,9 @@ export class CReader {
|
|
|
303
316
|
if (call.arguments.length != 2) this.fail(node, "scalar helper arity")
|
|
304
317
|
return this.binary(node, intent, this.expression(call.arguments[0], true), this.expression(call.arguments[1], true), call.name)
|
|
305
318
|
}
|
|
306
|
-
if (call.arguments.length != 2) this.fail(node, "semantic call arity")
|
|
307
319
|
const callee = this.identifier(call.name)
|
|
308
320
|
|
|
309
|
-
if (!this.generated && !this.
|
|
321
|
+
if (!this.generated && !this.declaredFunctionNames.has(callee)) this.fail(call.name, "implicit function declaration")
|
|
310
322
|
return withParserRanges({kind: "CallExpression", location, callee,
|
|
311
323
|
arguments: call.arguments.map((argument) => this.expression(argument, true))}, {callee: this.location(call.name)})
|
|
312
324
|
}
|
|
@@ -369,6 +381,11 @@ export class CReader {
|
|
|
369
381
|
if (node.type == "return_statement") {
|
|
370
382
|
const parts = this.parts(node)
|
|
371
383
|
|
|
384
|
+
if (parts.length == 2) {
|
|
385
|
+
this.shape(node, ["return", ";"])
|
|
386
|
+
if (!this.voidFunction) this.fail(node, "bare return requires a void function")
|
|
387
|
+
return {kind: "ReturnStatement", location}
|
|
388
|
+
}
|
|
372
389
|
this.shape(node, ["return", parts[1], ";"])
|
|
373
390
|
return {kind: "ReturnStatement", location, expression: this.expression(parts[1])}
|
|
374
391
|
}
|
|
@@ -403,13 +420,18 @@ export class CReader {
|
|
|
403
420
|
}
|
|
404
421
|
const call = this.call(expression)
|
|
405
422
|
|
|
406
|
-
if (
|
|
407
|
-
|
|
423
|
+
if (/^semantifold_print_(?:integer|boolean|string)$/u.test(this.text(call.name))) {
|
|
424
|
+
if (call.arguments.length != 1) this.fail(node, "print scaffold")
|
|
425
|
+
const value = this.expression(call.arguments[0])
|
|
408
426
|
|
|
409
|
-
|
|
427
|
+
const statement = {kind: /** @type {const} */ ("PrintStatement"), location, expression: value}
|
|
410
428
|
|
|
411
|
-
|
|
412
|
-
|
|
429
|
+
this.printTypes.set(statement, {node: call.name, type: this.text(call.name).slice("semantifold_print_".length)})
|
|
430
|
+
return statement
|
|
431
|
+
}
|
|
432
|
+
return withParserRanges({kind: /** @type {const} */ ("ExpressionStatement"), location,
|
|
433
|
+
expression: /** @type {import("../semantic/types.js").CallExpression} */ (this.expression(expression))},
|
|
434
|
+
{operator: this.location(this.parts(node)[1])})
|
|
413
435
|
}
|
|
414
436
|
return this.fail(node)
|
|
415
437
|
}
|
|
@@ -445,20 +467,98 @@ export class CReader {
|
|
|
445
467
|
const parameters = this.field(declarator, "parameters")
|
|
446
468
|
|
|
447
469
|
this.shape(declarator, [name, parameters])
|
|
448
|
-
const
|
|
470
|
+
const converted = this.parameterList(parameters)
|
|
471
|
+
const returnType = requireSourceReturnType("c", this.text(type), "function return", this.location(node), this.location(type))
|
|
472
|
+
const outerVoid = this.voidFunction
|
|
473
|
+
|
|
474
|
+
this.voidFunction = returnType.kind == "TypeReference" && returnType.name == "void"
|
|
475
|
+
const convertedBody = this.block(body)
|
|
476
|
+
|
|
477
|
+
this.voidFunction = outerVoid
|
|
478
|
+
return withParserRanges({kind: "FunctionDeclaration", location: this.location(node), name: this.identifier(name), parameters: converted,
|
|
479
|
+
returnType, body: convertedBody}, {name: this.location(name)})
|
|
480
|
+
}
|
|
449
481
|
|
|
450
|
-
|
|
451
|
-
|
|
482
|
+
/**
|
|
483
|
+
* Validates one exact required positional parameter list and converts it in source order.
|
|
484
|
+
* @param {CstNode} parameters - Parameter list node.
|
|
485
|
+
* @returns {import("../semantic/types.js").Parameter[]} Converted parameters.
|
|
486
|
+
*/
|
|
487
|
+
parameterList(parameters) {
|
|
488
|
+
const parts = this.parts(parameters)
|
|
489
|
+
const declarations = parts.slice(1, -1).filter((_part, index) => index % 2 == 0)
|
|
490
|
+
const voidOnly = declarations.length == 1 && parts.length == 3 && declarations[0].type == "parameter_declaration" &&
|
|
491
|
+
!declarations[0].children.some(({field}) => field == "declarator")
|
|
492
|
+
|
|
493
|
+
if (voidOnly) {
|
|
494
|
+
this.shape(parameters, ["(", "parameter_declaration", ")"])
|
|
495
|
+
if (this.text(declarations[0]) != "void") this.fail(declarations[0], "empty parameter list requires explicit void")
|
|
496
|
+
return []
|
|
497
|
+
}
|
|
498
|
+
if (declarations.length == 0) this.fail(parameters, "empty parameter list requires explicit void")
|
|
499
|
+
this.shape(parameters, ["(", ...declarations.flatMap((parameter, index) => index == 0 ? [parameter] : [",", parameter]), ")"])
|
|
500
|
+
return declarations.map((parameter) => {
|
|
452
501
|
const typeNode = this.field(parameter, "type")
|
|
453
502
|
const nameNode = this.field(parameter, "declarator")
|
|
454
503
|
|
|
455
504
|
this.shape(parameter, [typeNode, nameNode])
|
|
456
505
|
return withParserRanges({kind: /** @type {const} */ ("Parameter"), location: this.location(parameter), name: this.identifier(nameNode), type: this.type(typeNode)}, {name: this.location(nameNode)})
|
|
457
506
|
})
|
|
507
|
+
}
|
|
458
508
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
509
|
+
/**
|
|
510
|
+
* Recognizes a file-local function prototype declaration without descending further.
|
|
511
|
+
* @param {CstNode} node - Candidate translation-unit member.
|
|
512
|
+
* @returns {boolean} Whether this is a function prototype.
|
|
513
|
+
*/
|
|
514
|
+
isPrototype(node) {
|
|
515
|
+
if (node.type != "declaration") return false
|
|
516
|
+
const declarator = node.children.find(({field}) => field == "declarator")?.node
|
|
517
|
+
|
|
518
|
+
return declarator?.type == "function_declarator"
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Records one exact static prototype signature before any body conversion.
|
|
523
|
+
* @param {CstNode} node - Prototype declaration.
|
|
524
|
+
* @returns {void}
|
|
525
|
+
*/
|
|
526
|
+
recordPrototype(node) {
|
|
527
|
+
const type = this.field(node, "type")
|
|
528
|
+
const declarator = this.field(node, "declarator")
|
|
529
|
+
|
|
530
|
+
this.shape(node, ["storage_class_specifier", type, declarator, ";"])
|
|
531
|
+
if (this.text(this.parts(node)[0]) != "static" || declarator.type != "function_declarator") this.fail(node, "file-local prototype required")
|
|
532
|
+
const name = this.field(declarator, "declarator")
|
|
533
|
+
const parameters = this.field(declarator, "parameters")
|
|
534
|
+
|
|
535
|
+
this.shape(declarator, [name, parameters])
|
|
536
|
+
const converted = this.parameterList(parameters)
|
|
537
|
+
const nameText = this.identifier(name)
|
|
538
|
+
|
|
539
|
+
this.declaredFunctionNames.add(nameText)
|
|
540
|
+
this.prototypes.push({name: nameText, node,
|
|
541
|
+
parameterTypes: converted.map((parameter) => /** @type {import("../semantic/types.js").TypeReference} */ (parameter.type).name),
|
|
542
|
+
returnType: /** @type {import("../semantic/types.js").TypeReference} */ (requireSourceReturnType("c", this.text(type), "prototype return", this.location(node), this.location(type))).name})
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Confirms every recorded prototype agrees with its unique function definition.
|
|
547
|
+
* @param {import("../semantic/types.js").FunctionDeclaration[]} functions - Converted definitions.
|
|
548
|
+
* @returns {void}
|
|
549
|
+
*/
|
|
550
|
+
validatePrototypes(functions) {
|
|
551
|
+
const definitions = new Map(functions.map((declaration) => [declaration.name, declaration]))
|
|
552
|
+
|
|
553
|
+
for (const prototype of this.prototypes) {
|
|
554
|
+
const definition = definitions.get(prototype.name)
|
|
555
|
+
|
|
556
|
+
if (!definition) this.fail(prototype.node, "prototype without definition")
|
|
557
|
+
if (returnTypeName(definition.returnType) != prototype.returnType || definition.parameters.length != prototype.parameterTypes.length ||
|
|
558
|
+
definition.parameters.some((parameter, index) => returnTypeName(parameter.type) != prototype.parameterTypes[index])) {
|
|
559
|
+
this.fail(prototype.node, "prototype conflicts with function definition")
|
|
560
|
+
}
|
|
561
|
+
}
|
|
462
562
|
}
|
|
463
563
|
|
|
464
564
|
/**
|
|
@@ -511,17 +611,33 @@ export class CReader {
|
|
|
511
611
|
if (!this.generated) this.rejectScaffoldComments(root)
|
|
512
612
|
if (root.type != "translation_unit") this.fail(root, "translation unit required")
|
|
513
613
|
const parts = this.parts(root)
|
|
514
|
-
const
|
|
614
|
+
const members = parts.slice(1).filter((node) => !this.generated || node.type == "function_definition" || this.isPrototype(node))
|
|
515
615
|
|
|
516
|
-
if (parts.length < 3 || parts[0].type != "preproc_include" ||
|
|
616
|
+
if (parts.length < 3 || parts[0].type != "preproc_include" ||
|
|
617
|
+
members.some((node) => node.type != "function_definition" && !this.isPrototype(node))) this.fail(root, "translation unit members")
|
|
517
618
|
const include = parts[0]
|
|
518
619
|
const header = this.field(include, "path")
|
|
519
620
|
|
|
520
621
|
this.shape(include, ["#include", header])
|
|
521
622
|
if (header.type != "string_literal" || this.text(header) != '"semantifold_runtime.h"') this.fail(include, "support header include")
|
|
623
|
+
const definitions = members.filter((node) => node.type == "function_definition")
|
|
624
|
+
|
|
522
625
|
if (definitions.length < 2) this.fail(root, "semantic function and main required")
|
|
523
|
-
const
|
|
524
|
-
|
|
626
|
+
const mainNode = definitions[definitions.length - 1]
|
|
627
|
+
const functions = []
|
|
628
|
+
|
|
629
|
+
for (const node of members) {
|
|
630
|
+
if (node === mainNode) continue
|
|
631
|
+
if (node.type != "function_definition") {
|
|
632
|
+
this.recordPrototype(node)
|
|
633
|
+
continue
|
|
634
|
+
}
|
|
635
|
+
this.declaredFunctionNames.add(this.identifier(this.field(this.field(node, "declarator"), "declarator")))
|
|
636
|
+
functions.push(this.function(node))
|
|
637
|
+
}
|
|
638
|
+
this.validatePrototypes(functions)
|
|
639
|
+
const module = {kind: /** @type {const} */ ("Module"), location: this.location(root), functions,
|
|
640
|
+
entryPoint: this.main(mainNode)}
|
|
525
641
|
|
|
526
642
|
if (this.generated) validateNativeGraph(module)
|
|
527
643
|
const validated = validateParsedModule(module, "c")
|
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",
|