semantifold 0.11.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 +1 -1
- 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/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.js +2 -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/language-registry.d.ts.map +1 -1
- package/build/src/language-registry.js +16 -1
- package/build/src/semantic/validate.js +2 -2
- package/docs/c.md +1 -1
- package/docs/language-support.md +3 -2
- package/docs/testing.md +3 -2
- package/package.json +1 -1
- package/src/backends/c.js +23 -4
- package/src/backends/cpp.js +1 -0
- package/src/backends/ordered-expressions.js +46 -10
- package/src/backends/shared.js +1 -1
- package/src/frontends/c.js +135 -19
- package/src/language-registry.js +15 -0
- package/src/semantic/validate.js +1 -1
package/docs/c.md
CHANGED
|
@@ -28,7 +28,7 @@ Canonical `main` calls `semantifold_cleanup()` once after semantic entry stateme
|
|
|
28
28
|
|
|
29
29
|
The unchanged root-bundled private legacy boundary supplies recursively frozen C CST data from exact `tree-sitter@0.21.1` and `tree-sitter-c@0.23.2`. `src/frontends/c-parser.js` describes that private plain-data boundary; native handles and parser-specific objects never enter semantic values. Every child, field, anonymous token, extra, error, missing node and recovery flag is traversed before meaning is returned. Coordinates and token origins refer to the original UTF-16 source, including astral characters and CRLF. There is no parser-source scan, compiler-based interpretation, preprocessing fallback or source-text recovery.
|
|
30
30
|
|
|
31
|
-
Ordinary source contains the exact support-header include, at least one `static` semantic function
|
|
31
|
+
Ordinary source contains the exact support-header include, at least one `static` semantic function plus final `int main(void)`. Task 005 semantic functions accept any number of explicitly typed named parameters, including zero — spelled `void` for an empty list — and return exact scalars or `void`. A file-scope `static` prototype may precede a function definition for forward and mutual recursion; every prototype must agree exactly with its unique definition, and a prototype without a definition is rejected. Bodies admit initialized single scalar locals, plain assignment, prints through the matching scalar helper, explicit returns and braced conditionals, including `else if`. A non-void function returns a value on every reachable path; a void function may fall through or use a bare `return;`, and a bare return in a non-void function is rejected. A void call is legal only as a standalone statement. `const` locals are immutable; other locals are mutable, and parameters are immutable. Main ends with the exact cleanup call and `return 0`. Raw calls must refer to a preceding function definition or prototype; caller-authored forward calls requiring implicit C declarations are rejected, and canonical generated prototypes support forward calls and recursion.
|
|
32
32
|
|
|
33
33
|
Only direct semantic calls and the exact scalar/literal/print helpers are recognized. Caller-authored calls nested in an operator operand or another call's argument are rejected because ordinary C does not establish the required semantic evaluation order. Trigraph preprocessing in parser-backed comments or string content is rejected. No user preprocessing, typedef substitution, global data, complex/multiple declarator, qualifier beyond local `const`, array, struct, union, enum, pointer, function pointer, cast, address/dereference, member access, ownership call, `sizeof`, comma/ternary, loop, switch, goto, update, compound operator, conversion or arbitrary external call is accepted.
|
|
34
34
|
|
package/docs/language-support.md
CHANGED
|
@@ -84,7 +84,7 @@ The shared negative corpus covers parser recovery, missing types, truthiness, mi
|
|
|
84
84
|
|
|
85
85
|
## Task 005 general functions and calls
|
|
86
86
|
|
|
87
|
-
PHP, Ruby, JavaScript with JSDoc, TypeScript, Java, Dart, and Zig accept any number of required positional parameters, including zero. The module resolver collects every unique function signature before validating bodies, assigns declarations deterministic `function:N` identities, and binds each direct call to that identity plus its ordered parameter and return types. This permits forward calls, nested calls, direct recursion, and unambiguous mutual recursion without overload or dynamic dispatch. Arity and every positional scalar type must match exactly.
|
|
87
|
+
PHP, Ruby, JavaScript with JSDoc, TypeScript, Java, C, Dart, and Zig accept any number of required positional parameters, including zero. The module resolver collects every unique function signature before validating bodies, assigns declarations deterministic `function:N` identities, and binds each direct call to that identity plus its ordered parameter and return types. This permits forward calls, nested calls, direct recursion, and unambiguous mutual recursion without overload or dynamic dispatch. Arity and every positional scalar type must match exactly.
|
|
88
88
|
|
|
89
89
|
Function returns are `integer`, `boolean`, `string`, or `void`. A non-void function has an expression on every reachable return and returns on every reachable path. A void function may fall through or use a bare return, and its call is legal only as an `ExpressionStatement`. Void is never a value: it cannot type a parameter or local, initialize or assign a binding, become an argument or operand, be printed, or be returned as a value. Generation revalidates declaration identities and complete resolved signatures in externally supplied IR. Function names, parameters, return annotations, callees, arguments, and resolution references retain deterministic parser-backed provenance and mappings.
|
|
90
90
|
|
|
@@ -94,10 +94,11 @@ The source profiles remain deliberately exact:
|
|
|
94
94
|
- JavaScript accepts only declarations spelled `function name(...)` with a JSDoc annotation for every identifier parameter and an explicit scalar or `void` return annotation. TypeScript requires the corresponding explicit keyword annotations. Both reject optional/default/rest/destructured parameters, overloads, async/generators, arrows/function expressions, methods, callbacks and higher-order callees, optional/receiver/spread calls, and general TypeScript assertions. Task 012 adds only its exact generic declaration/application profile; Task 006 has one type-only exception for the exact independently validated `Map#get(...)!` round-trip form.
|
|
95
95
|
- PHP accepts simple typed by-value required parameters, an explicit scalar or `void` return, and unqualified direct calls. Defaults, variadics, named/unpacked/by-reference arguments, by-reference parameters or returns, methods, dynamic/qualified calls, implicit nullability, and missing types are rejected; Task 007 adds only its exact explicit nullable profile.
|
|
96
96
|
- Java accepts the established private-static semantic method profile with explicit supported parameter and scalar-or-void result types inside `public final class Main`, plus receiver-free direct calls and the exact entry method. Generated zero-parameter signatures named `getClass`, `hashCode`, `clone`, `toString`, `notify`, `notifyAll`, `wait`, or `finalize` conflict with inherited `java.lang.Object` instance methods and are rejected before emission; nonconflicting scalar-parameter overloads of those names remain available. Overload sets, instance/receiver/qualified/static-import ambiguity, varargs, bounded/defaulted/wildcard/raw generics, constructors, throws clauses, and non-static semantic methods are rejected.
|
|
97
|
+
- C accepts file-scope `static` semantic functions with any number of explicitly typed named parameters — spelled `void` for an empty list — exact scalar or `void` returns, and unqualified direct calls. A `static` prototype may precede a definition for forward or mutual recursion and must agree exactly with its unique definition; a prototype without a definition, a conflicting prototype, an implicit declaration, a bare return in a non-void function, or `void` used as a parameter, local, argument, operand or printed value is rejected. Generated zero-parameter signatures spell `(void)` and generated prototypes make forward calls and recursion compilable under `-Wstrict-prototypes`/`-Wmissing-prototypes`. See [C source and target](c.md).
|
|
97
98
|
- Dart accepts synchronous top-level functions with exact explicit `int`, `bool`, `String`, or `void` returns, explicitly typed required positional parameters, initialized typed locals, direct calls, and exact `void main()`/`print`. Inference, `var`, inferred `final`, optional/named/default arguments, methods/classes, generics, imports, dynamic/nullability, collections, exceptions, async, and isolates are rejected. Generated Dart alone may contain formatter-added trailing commas and the complete canonical private runtime prefix; neither grants those forms to ordinary source. See [Dart source and target](dart.md).
|
|
98
99
|
- Zig accepts top-level functions with exact explicit `i64`, `bool`, `[]const u8`, or `void` returns, explicitly typed required positional parameters, initialized typed `const`/`var` locals, direct calls, and the exact generated support/`pub fn main()` shell. Inference, optional/default/rest parameters, methods, containers, user slices/pointers/allocators, imports beyond the canonical standard-library support, error unions, `comptime`, generics, reflection, FFI, async, and concurrency are rejected. See [Zig source and target](zig.md).
|
|
99
100
|
|
|
100
|
-
Python, C#, C
|
|
101
|
+
Python, C#, C++, Rust, Swift, Kotlin/JVM, Go, and browser Wasm remain on their Task 025 Tasks 001–004 profile. Their registry feature flag is false, their existing exact-two/scalar-return behavior is unchanged, and a Task-005-only arity, void return, or void call statement is a located `UNSUPPORTED_CAPABILITY` before source, bytes, or a partial artifact set is exposed. This boundary does not broaden the future Task 013 aggregate acceptance.
|
|
101
102
|
|
|
102
103
|
## Task 006 immutable lists and maps
|
|
103
104
|
|
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
|
|
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
|
|
|
@@ -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"])
|
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/language-registry.js
CHANGED
|
@@ -571,6 +571,21 @@ const records = [
|
|
|
571
571
|
artifactMultiplicity: "multiple",
|
|
572
572
|
check: {factory: createCCheckPlan, stages: ["compile", "link"], toolchains: ["clang"]},
|
|
573
573
|
defaultFilename: "program.c",
|
|
574
|
+
features: {
|
|
575
|
+
closedRecords: false,
|
|
576
|
+
conditionControlledLoops: false,
|
|
577
|
+
conditionalExpressions: false,
|
|
578
|
+
stringInterpolation: false,
|
|
579
|
+
effectfulCapabilitiesAndResources: false,
|
|
580
|
+
generalFunctionsAndCalls: true,
|
|
581
|
+
immutableCollections: false,
|
|
582
|
+
optionalValues: false,
|
|
583
|
+
orderedListIteration: false,
|
|
584
|
+
orderedMapIteration: false,
|
|
585
|
+
referenceClasses: false,
|
|
586
|
+
typedErrors: false,
|
|
587
|
+
typeParametersAndGenerics: false
|
|
588
|
+
},
|
|
574
589
|
frontend: parseC,
|
|
575
590
|
id: "c",
|
|
576
591
|
mediaType: "text/x-c",
|
package/src/semantic/validate.js
CHANGED
|
@@ -8,7 +8,7 @@ import {parserRangeFor} from "./provenance.js"
|
|
|
8
8
|
import {validateEffectGraph} from "./effects.js"
|
|
9
9
|
import {validateResourceLifetimes} from "./lifetime.js"
|
|
10
10
|
|
|
11
|
-
const task005Languages = new Set(["php", "ruby", "javascript", "typescript", "java", "dart", "zig"])
|
|
11
|
+
const task005Languages = new Set(["php", "ruby", "javascript", "typescript", "java", "c", "dart", "zig"])
|
|
12
12
|
|
|
13
13
|
/** @typedef {Map<string, import("./types.js").RecordDeclaration>} RecordRegistry */
|
|
14
14
|
/** @typedef {Map<string, import("./types.js").ClassDeclaration>} ClassRegistry */
|