semantifold 0.4.0 → 0.6.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 +13 -7
- package/build/index.d.ts +12 -8
- package/build/index.d.ts.map +1 -1
- package/build/index.js +10 -7
- package/build/src/backends/android.d.ts +49 -0
- package/build/src/backends/android.d.ts.map +1 -0
- package/build/src/backends/android.js +1278 -0
- package/build/src/backends/identifiers.d.ts +6 -0
- package/build/src/backends/identifiers.d.ts.map +1 -1
- package/build/src/backends/identifiers.js +30 -1
- package/build/src/backends/ios.js +2 -2
- package/build/src/backends/kotlin.d.ts +10 -0
- package/build/src/backends/kotlin.d.ts.map +1 -1
- package/build/src/backends/kotlin.js +11 -9
- package/build/src/backends/native-validation.d.ts +2 -2
- package/build/src/backends/native-validation.d.ts.map +1 -1
- package/build/src/backends/native-validation.js +2 -2
- package/build/src/backends/program.d.ts +6 -4
- package/build/src/backends/program.d.ts.map +1 -1
- package/build/src/backends/program.js +5 -4
- package/build/src/backends/scalars.d.ts.map +1 -1
- package/build/src/backends/scalars.js +2 -1
- package/build/src/backends/shared.d.ts.map +1 -1
- package/build/src/backends/shared.js +20 -4
- package/build/src/backends/writer.d.ts +21 -1
- package/build/src/backends/writer.d.ts.map +1 -1
- package/build/src/backends/writer.js +25 -2
- package/build/src/backends/zig-runtime.d.ts +5 -0
- package/build/src/backends/zig-runtime.d.ts.map +1 -0
- package/build/src/backends/zig-runtime.js +92 -0
- package/build/src/backends/zig-validation.d.ts +9 -0
- package/build/src/backends/zig-validation.d.ts.map +1 -0
- package/build/src/backends/zig-validation.js +73 -0
- package/build/src/backends/zig.d.ts +42 -0
- package/build/src/backends/zig.d.ts.map +1 -0
- package/build/src/backends/zig.js +584 -0
- package/build/src/frontends/scalars.d.ts.map +1 -1
- package/build/src/frontends/scalars.js +2 -1
- package/build/src/frontends/zig-parser.d.ts +31 -0
- package/build/src/frontends/zig-parser.d.ts.map +1 -0
- package/build/src/frontends/zig-parser.js +17 -0
- package/build/src/frontends/zig.d.ts +14 -0
- package/build/src/frontends/zig.d.ts.map +1 -0
- package/build/src/frontends/zig.js +970 -0
- package/build/src/language-registry.d.ts.map +1 -1
- package/build/src/language-registry.js +40 -1
- package/build/src/mapping.js +2 -2
- package/build/src/semantic/provenance.js +3 -3
- package/build/src/semantic/types.d.ts +184 -4
- package/build/src/semantic/types.d.ts.map +1 -1
- package/build/src/semantic/types.js +45 -3
- package/build/src/semantic/validate.js +2 -2
- package/build/src/toolchains.d.ts +6 -0
- package/build/src/toolchains.d.ts.map +1 -1
- package/build/src/toolchains.js +2 -1
- package/build/src/zig-bindings.d.ts +10 -0
- package/build/src/zig-bindings.d.ts.map +1 -0
- package/build/src/zig-bindings.js +54 -0
- package/docs/android.md +79 -0
- package/docs/architecture.md +13 -5
- package/docs/goals.md +1 -1
- package/docs/ios.md +1 -1
- package/docs/language-support.md +15 -4
- package/docs/parser-qualification.md +31 -1
- package/docs/source-maps.md +7 -3
- package/docs/specification.md +5 -0
- package/docs/testing.md +40 -3
- package/docs/zig.md +33 -0
- package/index.js +9 -6
- package/node_modules/semantifold-tree-sitter-zig-internal/LICENSE +15 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/README.md +5 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/LICENSE +21 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/README.md +21 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/binding.gyp +29 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/bindings/node/binding.cc +20 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/bindings/node/binding_test.js +9 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/bindings/node/index.d.ts +28 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/bindings/node/index.js +11 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/grammar.js +900 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/package.json +57 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/prebuilds/darwin-arm64/@tree-sitter-grammars+tree-sitter-zig.node +0 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/prebuilds/darwin-x64/@tree-sitter-grammars+tree-sitter-zig.node +0 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/prebuilds/linux-arm64/@tree-sitter-grammars+tree-sitter-zig.node +0 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/prebuilds/linux-x64/@tree-sitter-grammars+tree-sitter-zig.node +0 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/prebuilds/win32-arm64/@tree-sitter-grammars+tree-sitter-zig.node +0 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/prebuilds/win32-x64/@tree-sitter-grammars+tree-sitter-zig.node +0 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/queries/folds.scm +23 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/queries/highlights.scm +291 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/queries/indents.scm +22 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/queries/injections.scm +10 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/src/grammar.json +6570 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/src/node-types.json +3178 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/src/parser.c +169829 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/src/tree_sitter/alloc.h +54 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/src/tree_sitter/array.h +291 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/src/tree_sitter/parser.h +266 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/tree-sitter-zig.wasm +0 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/@tree-sitter-grammars/tree-sitter-zig/tree-sitter.json +38 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/LICENSE +21 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/README.md +128 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/binding.gyp +80 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/index.js +916 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/package.json +76 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/prebuilds/darwin-arm64/tree-sitter.node +0 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/prebuilds/darwin-x64/tree-sitter.node +0 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/prebuilds/linux-arm64/tree-sitter.node +0 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/prebuilds/linux-x64/tree-sitter.node +0 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/prebuilds/win32-arm64/tree-sitter.node +0 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/prebuilds/win32-x64/tree-sitter.node +0 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/src/addon_data.h +47 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/src/binding.cc +35 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/src/conversions.cc +140 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/src/conversions.h +22 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/src/language.cc +106 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/src/language.h +17 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/src/logger.cc +70 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/src/logger.h +19 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/src/lookaheaditerator.cc +122 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/src/lookaheaditerator.h +33 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/src/node.cc +1088 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/src/node.h +30 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/src/parser.cc +306 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/src/parser.h +35 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/src/query.cc +397 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/src/query.h +40 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/src/tree.cc +316 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/src/tree.h +45 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/src/tree_cursor.cc +213 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/src/tree_cursor.h +52 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/tree-sitter.d.ts +1042 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/include/tree_sitter/api.h +1318 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/alloc.c +48 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/alloc.h +41 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/array.h +291 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/atomic.h +68 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/clock.h +146 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/error_costs.h +11 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/get_changed_ranges.c +501 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/get_changed_ranges.h +36 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/host.h +21 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/language.c +223 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/language.h +297 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/length.h +52 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/lexer.c +435 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/lexer.h +49 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/lib.c +12 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/node.c +920 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/parser.c +2165 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/parser.h +266 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/point.h +62 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/portable/endian.h +206 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/query.c +4187 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/reduce_action.h +34 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/reusable_node.h +95 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/stack.c +899 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/stack.h +130 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/subtree.c +1066 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/subtree.h +399 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/tree.c +170 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/tree.h +31 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/tree_cursor.c +712 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/tree_cursor.h +48 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/ts_assert.h +11 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/ICU_SHA +1 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/LICENSE +414 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/README.md +29 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/ptypes.h +1 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/umachine.h +448 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/urename.h +1 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/utf.h +1 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/utf16.h +733 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/utf8.h +881 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode.h +83 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/wasm/stdlib-symbols.txt +24 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/wasm/stdlib.c +109 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/wasm/wasm-stdlib.h +1302 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/wasm_store.c +1859 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/node_modules/tree-sitter/vendor/tree-sitter/lib/src/wasm_store.h +31 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/package.json +21 -0
- package/node_modules/semantifold-tree-sitter-zig-internal/src/zig.js +134 -0
- package/package.json +11 -6
- package/src/backends/android.js +1374 -0
- package/src/backends/identifiers.js +30 -0
- package/src/backends/ios.js +1 -1
- package/src/backends/kotlin.js +11 -8
- package/src/backends/native-validation.js +1 -1
- package/src/backends/program.js +4 -3
- package/src/backends/scalars.js +1 -0
- package/src/backends/shared.js +22 -3
- package/src/backends/writer.js +27 -2
- package/src/backends/zig-runtime.js +93 -0
- package/src/backends/zig-validation.js +68 -0
- package/src/backends/zig.js +579 -0
- package/src/frontends/scalars.js +1 -0
- package/src/frontends/zig-parser.js +20 -0
- package/src/frontends/zig.js +994 -0
- package/src/language-registry.js +40 -0
- package/src/mapping.js +1 -1
- package/src/semantic/provenance.js +2 -2
- package/src/semantic/types.js +47 -2
- package/src/semantic/validate.js +1 -1
- package/src/toolchains.js +1 -0
- package/src/zig-bindings.js +46 -0
|
@@ -0,0 +1,994 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import {isZigIdentifier} from "../backends/identifiers.js"
|
|
4
|
+
import {generateZigProject} from "../backends/zig.js"
|
|
5
|
+
import {zigRuntime} from "../backends/zig-runtime.js"
|
|
6
|
+
import {maximumZigSourceLength} from "../backends/zig-validation.js"
|
|
7
|
+
import {missingType, SemantifoldDiagnostic, unsupportedSyntax} from "../diagnostic.js"
|
|
8
|
+
import {createCoordinateIndex, indexedPointAt} from "../semantic/location.js"
|
|
9
|
+
import {withAdaptedOperation} from "../semantic/operators.js"
|
|
10
|
+
import {withParserRanges} from "../semantic/provenance.js"
|
|
11
|
+
import {hasOnlyUnicodeScalars} from "../semantic/scalars.js"
|
|
12
|
+
import {validateParsedModule} from "../semantic/validate.js"
|
|
13
|
+
import {sourceScalarType} from "./scalars.js"
|
|
14
|
+
import {parseZigCst} from "./zig-parser.js"
|
|
15
|
+
import {collectZigBindingUsage} from "../zig-bindings.js"
|
|
16
|
+
|
|
17
|
+
/** @typedef {import("./zig-parser.js").CstNode} CstNode */
|
|
18
|
+
/** @typedef {import("../semantic/types.js").Expression} Expression */
|
|
19
|
+
/** @typedef {import("../semantic/types.js").Statement} Statement */
|
|
20
|
+
/** @typedef {import("../semantic/types.js").FunctionReturnTypeName} Scalar */
|
|
21
|
+
|
|
22
|
+
const binaryOperations = new Map([
|
|
23
|
+
["+", "Add"], ["-", "Subtract"], ["*", "Multiply"], ["==", "Equal"], ["!=", "NotEqual"],
|
|
24
|
+
["<", "LessThan"], ["<=", "LessThanOrEqual"], [">", "GreaterThan"], [">=", "GreaterThanOrEqual"],
|
|
25
|
+
["and", "And"], ["or", "Or"]
|
|
26
|
+
])
|
|
27
|
+
const helperOperations = new Map([
|
|
28
|
+
["semantifold_integer_add", "Add"], ["semantifold_integer_subtract", "Subtract"],
|
|
29
|
+
["semantifold_integer_multiply", "Multiply"], ["semantifold_string_concat", "Add"],
|
|
30
|
+
["semantifold_string_equal", "Equal"], ["semantifold_string_not_equal", "NotEqual"]
|
|
31
|
+
])
|
|
32
|
+
const operationSpellings = new Map([
|
|
33
|
+
["IntegerAdd", "semantifold_integer_add"], ["IntegerSubtract", "semantifold_integer_subtract"],
|
|
34
|
+
["IntegerMultiply", "semantifold_integer_multiply"], ["IntegerNegate", "semantifold_integer_negate"],
|
|
35
|
+
["IntegerEqual", "=="], ["IntegerNotEqual", "!="], ["IntegerLessThan", "<"],
|
|
36
|
+
["IntegerLessThanOrEqual", "<="], ["IntegerGreaterThan", ">"], ["IntegerGreaterThanOrEqual", ">="],
|
|
37
|
+
["BooleanEqual", "=="], ["BooleanNotEqual", "!="], ["BooleanAnd", "and"], ["BooleanOr", "or"], ["BooleanNot", "!"],
|
|
38
|
+
["StringConcat", "semantifold_string_concat"], ["StringEqual", "semantifold_string_equal"],
|
|
39
|
+
["StringNotEqual", "semantifold_string_not_equal"]
|
|
40
|
+
])
|
|
41
|
+
|
|
42
|
+
/** Consumes the complete qualified Zig CST and retains exact generated-shell constraints. */
|
|
43
|
+
class ZigReader {
|
|
44
|
+
/**
|
|
45
|
+
* Creates a reader over one complete parser snapshot.
|
|
46
|
+
* @param {string} filename - Logical source filename.
|
|
47
|
+
* @param {string} source - Exact Zig source text.
|
|
48
|
+
*/
|
|
49
|
+
constructor(filename, source) {
|
|
50
|
+
this.filename = filename
|
|
51
|
+
this.source = source
|
|
52
|
+
this.coordinates = createCoordinateIndex(source)
|
|
53
|
+
this.generated = false
|
|
54
|
+
/** @type {Map<string, Expression> | undefined} */
|
|
55
|
+
this.temporaryExpressions = undefined
|
|
56
|
+
/** @type {Set<string> | undefined} */
|
|
57
|
+
this.temporaryNames = undefined
|
|
58
|
+
/** @type {Map<Expression, {node: CstNode, spelling: string}>} */
|
|
59
|
+
this.operations = new Map()
|
|
60
|
+
/** @type {Map<Statement, {node: CstNode, type: string}>} */
|
|
61
|
+
this.prints = new Map()
|
|
62
|
+
/** @type {Map<string, Scalar>} */
|
|
63
|
+
this.functions = new Map()
|
|
64
|
+
/** @type {Map<import("../semantic/types.js").Parameter | import("../semantic/types.js").LocalDeclaration, "use" | "address" | "none">} */
|
|
65
|
+
this.scaffolds = new Map()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Converts a parser node boundary to a semantic location.
|
|
70
|
+
* @param {CstNode} node - Parser node.
|
|
71
|
+
* @returns {import("../semantic/types.js").SourceLocation} UTF-16 location.
|
|
72
|
+
*/
|
|
73
|
+
location(node) {
|
|
74
|
+
return {filename: this.filename, start: indexedPointAt(this.coordinates, node.startIndex), end: indexedPointAt(this.coordinates, node.endIndex)}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Reads the exact source spelling owned by a parser node.
|
|
79
|
+
* @param {CstNode} node - Parser node.
|
|
80
|
+
* @returns {string} Node source text.
|
|
81
|
+
*/
|
|
82
|
+
text(node) {
|
|
83
|
+
return this.source.slice(node.startIndex, node.endIndex)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Reads one generated line-comment marker independent of the source newline convention.
|
|
88
|
+
* @param {CstNode} node - Marker comment node.
|
|
89
|
+
* @returns {string} Marker spelling without a parser-owned trailing carriage return.
|
|
90
|
+
*/
|
|
91
|
+
marker(node) {
|
|
92
|
+
return this.text(node).replace(/\r$/u, "")
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Raises a located unsupported-syntax diagnostic.
|
|
97
|
+
* @param {CstNode} node - Rejected parser node.
|
|
98
|
+
* @param {string} [detail] - Diagnostic detail.
|
|
99
|
+
* @returns {never} This function always throws.
|
|
100
|
+
*/
|
|
101
|
+
fail(node, detail = node.type) {
|
|
102
|
+
unsupportedSyntax("zig", detail, this.location(node))
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Exhaustively rejects recovery, unexpected extras, directives, and excessive nesting.
|
|
107
|
+
* @param {CstNode} node - Current CST node.
|
|
108
|
+
* @param {number} [depth] - Current nesting depth.
|
|
109
|
+
*/
|
|
110
|
+
validateTree(node, depth = 0) {
|
|
111
|
+
if (depth > 512) this.fail(node, "Zig CST exceeds the 512-level traversal limit")
|
|
112
|
+
for (const {node: child} of node.children) this.validateTree(child, depth + 1)
|
|
113
|
+
if (node.error || node.missing || node.hasError) {
|
|
114
|
+
throw new SemantifoldDiagnostic({code: "PARSE_ERROR", language: "zig", location: this.location(node), message: `Zig parser recovery at '${node.type}'.`})
|
|
115
|
+
}
|
|
116
|
+
if (node.extra && node.type != "comment") this.fail(node, "unexpected extra parser child")
|
|
117
|
+
if (node.type == "comment" && (/^\s*\/\/[/!]/u.test(this.text(node)) || /^\s*\/\/\s*zig fmt:/u.test(this.text(node)))) {
|
|
118
|
+
this.fail(node, "doc or formatter-directive comment")
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Returns every non-comment child without hiding syntax edges.
|
|
124
|
+
* @param {CstNode} node - Parent parser node.
|
|
125
|
+
* @returns {CstNode[]} Ordered non-comment children.
|
|
126
|
+
*/
|
|
127
|
+
parts(node) {
|
|
128
|
+
return node.children.map(({node: child}) => child).filter(child => child.type != "comment")
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Retains compiler-owned scaffold markers while ignoring ordinary inert comments.
|
|
133
|
+
* @param {CstNode} node - Parent parser node.
|
|
134
|
+
* @returns {CstNode[]} Complete significant child list.
|
|
135
|
+
*/
|
|
136
|
+
significant(node) {
|
|
137
|
+
return node.children.map(({node: child}) => child).filter(child =>
|
|
138
|
+
child.type != "comment" || /semantifold:/u.test(this.text(child)))
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Requires exactly one parser field edge.
|
|
143
|
+
* @param {CstNode} node - Parent parser node.
|
|
144
|
+
* @param {string} name - Required field name.
|
|
145
|
+
* @returns {CstNode} Field child.
|
|
146
|
+
*/
|
|
147
|
+
field(node, name) {
|
|
148
|
+
const edges = node.children.filter(({field}) => field == name)
|
|
149
|
+
|
|
150
|
+
if (edges.length != 1) this.fail(node, `expected one '${name}' field`)
|
|
151
|
+
return edges[0].node
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Requires an exact ordered child/token shape.
|
|
156
|
+
* @param {CstNode} node - Parser node to check.
|
|
157
|
+
* @param {(CstNode | string)[]} expected - Expected node identities or token types.
|
|
158
|
+
*/
|
|
159
|
+
shape(node, expected) {
|
|
160
|
+
const parts = this.parts(node)
|
|
161
|
+
|
|
162
|
+
if (parts.length != expected.length || parts.some((child, index) =>
|
|
163
|
+
typeof expected[index] == "string" ? child.type != expected[index] || !child.named && this.text(child) != expected[index] : child !== expected[index])) {
|
|
164
|
+
this.fail(node, `${node.type} child shape`)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Validates and returns one ordinary Zig identifier.
|
|
170
|
+
* @param {CstNode} node - Candidate identifier node.
|
|
171
|
+
* @returns {string} Validated identifier.
|
|
172
|
+
*/
|
|
173
|
+
identifier(node) {
|
|
174
|
+
const name = this.text(node)
|
|
175
|
+
|
|
176
|
+
if (node.type != "identifier" || !isZigIdentifier(name)) this.fail(node, "reserved or unsupported identifier")
|
|
177
|
+
return name
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Converts an exact source scalar annotation.
|
|
182
|
+
* @param {CstNode} node - Type node.
|
|
183
|
+
* @param {boolean} [allowVoid] - Whether void is valid here.
|
|
184
|
+
* @returns {import("../semantic/types.js").SemanticFunctionReturnType} Semantic type.
|
|
185
|
+
*/
|
|
186
|
+
type(node, allowVoid = false) {
|
|
187
|
+
const spelling = this.text(node)
|
|
188
|
+
|
|
189
|
+
if (allowVoid && node.type == "builtin_type" && spelling == "void") {
|
|
190
|
+
this.shape(node, ["void"])
|
|
191
|
+
return withParserRanges({kind: "TypeReference", name: "void"}, {type: this.location(node)})
|
|
192
|
+
}
|
|
193
|
+
if (node.type == "builtin_type" && ["i64", "bool"].includes(spelling)) {
|
|
194
|
+
if (spelling == "bool") this.shape(node, ["bool"])
|
|
195
|
+
else if (node.children.length) this.fail(node, "scalar type shape")
|
|
196
|
+
} else if (node.type == "slice_type" && spelling == "[]const u8") {
|
|
197
|
+
this.shape(node, ["[", "]", "const", this.parts(node)[3]])
|
|
198
|
+
const child = this.parts(node)[3]
|
|
199
|
+
|
|
200
|
+
if (child.type != "builtin_type" || this.text(child) != "u8" || child.children.length) this.fail(child, "immutable UTF-8 slice element")
|
|
201
|
+
} else return missingType("zig", allowVoid ? "function return" : "scalar", this.location(node))
|
|
202
|
+
const type = sourceScalarType("zig", spelling, this.location(node))
|
|
203
|
+
|
|
204
|
+
if (!type) return missingType("zig", "scalar", this.location(node))
|
|
205
|
+
return type
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Decomposes an exact direct call.
|
|
210
|
+
* @param {CstNode} node - Call expression node.
|
|
211
|
+
* @returns {{name: CstNode, arguments: CstNode[]}} Callee and ordered arguments.
|
|
212
|
+
*/
|
|
213
|
+
call(node) {
|
|
214
|
+
if (node.type != "call_expression") this.fail(node, "direct call required")
|
|
215
|
+
const name = this.field(node, "function")
|
|
216
|
+
const parts = this.parts(node)
|
|
217
|
+
const argumentsAndCommas = parts.slice(2, -1)
|
|
218
|
+
const args = argumentsAndCommas.filter((_part, index) => index % 2 == 0)
|
|
219
|
+
|
|
220
|
+
this.shape(node, [name, "(", ...args.flatMap((argument, index) => index ? [",", argument] : [argument]), ")"])
|
|
221
|
+
return {name, arguments: args}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Decodes one bounded ordinary Zig string literal.
|
|
226
|
+
* @param {CstNode} node - String node.
|
|
227
|
+
* @returns {string} Unicode scalar value.
|
|
228
|
+
*/
|
|
229
|
+
string(node) {
|
|
230
|
+
if (node.type != "string") this.fail(node, "ordinary UTF-8 string literal required")
|
|
231
|
+
const parts = this.parts(node)
|
|
232
|
+
|
|
233
|
+
this.shape(node, ['"', ...parts.slice(1, -1), '"'])
|
|
234
|
+
let value = ""
|
|
235
|
+
|
|
236
|
+
for (const part of parts.slice(1, -1)) {
|
|
237
|
+
const spelling = this.text(part)
|
|
238
|
+
|
|
239
|
+
if (part.type == "string_content") {
|
|
240
|
+
if (/[\r\n]/u.test(spelling)) this.fail(part, "raw string line terminator")
|
|
241
|
+
value += spelling
|
|
242
|
+
} else if (part.type == "escape_sequence") {
|
|
243
|
+
const simple = new Map([["\\n", "\n"], ["\\r", "\r"], ["\\t", "\t"], ["\\\\", "\\"], ['\\"', '"']])
|
|
244
|
+
|
|
245
|
+
if (simple.has(spelling)) value += simple.get(spelling)
|
|
246
|
+
else if (/^\\x[0-9a-fA-F]{2}$/u.test(spelling)) {
|
|
247
|
+
const scalar = Number.parseInt(spelling.slice(2), 16)
|
|
248
|
+
|
|
249
|
+
if (scalar > 127) this.fail(part, "non-ASCII byte escape")
|
|
250
|
+
value += String.fromCodePoint(scalar)
|
|
251
|
+
} else if (/^\\u\{[0-9a-fA-F]{1,6}\}$/u.test(spelling)) {
|
|
252
|
+
const scalar = Number.parseInt(spelling.slice(3, -1), 16)
|
|
253
|
+
|
|
254
|
+
if (scalar > 0x10FFFF || scalar >= 0xD800 && scalar <= 0xDFFF) this.fail(part, "non-scalar Unicode escape")
|
|
255
|
+
value += String.fromCodePoint(scalar)
|
|
256
|
+
} else this.fail(part, "unsupported string escape")
|
|
257
|
+
} else this.fail(part, "string child")
|
|
258
|
+
}
|
|
259
|
+
if (!hasOnlyUnicodeScalars(value)) this.fail(node, "non-scalar string value")
|
|
260
|
+
return value
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Converts one supported scalar expression.
|
|
265
|
+
* @param {CstNode} node - Expression node.
|
|
266
|
+
* @returns {Expression} Semantic expression.
|
|
267
|
+
*/
|
|
268
|
+
expression(node) {
|
|
269
|
+
const location = this.location(node)
|
|
270
|
+
|
|
271
|
+
if (node.type == "parenthesized_expression") {
|
|
272
|
+
const parts = this.parts(node)
|
|
273
|
+
|
|
274
|
+
this.shape(node, ["(", parts[1], ")"])
|
|
275
|
+
return this.expression(parts[1])
|
|
276
|
+
}
|
|
277
|
+
if (node.type == "identifier") {
|
|
278
|
+
const temporary = this.temporaryExpressions?.get(this.text(node))
|
|
279
|
+
|
|
280
|
+
if (temporary) {
|
|
281
|
+
this.temporaryExpressions?.delete(this.text(node))
|
|
282
|
+
return temporary
|
|
283
|
+
}
|
|
284
|
+
return withParserRanges({kind: "IdentifierExpression", location, name: this.identifier(node)}, {name: location})
|
|
285
|
+
}
|
|
286
|
+
if (node.type == "integer") {
|
|
287
|
+
const spelling = this.text(node)
|
|
288
|
+
const value = Number(spelling)
|
|
289
|
+
|
|
290
|
+
if (!/^(?:0|[1-9][0-9]*)$/u.test(spelling) || !Number.isSafeInteger(value)) this.fail(node, "non-safe or noncanonical i64 literal")
|
|
291
|
+
return withParserRanges({kind: "IntegerLiteral", location, value}, {literal: location})
|
|
292
|
+
}
|
|
293
|
+
if (node.type == "boolean") {
|
|
294
|
+
const spelling = this.text(node)
|
|
295
|
+
|
|
296
|
+
if (spelling != "true" && spelling != "false") this.fail(node, "Boolean literal spelling")
|
|
297
|
+
this.shape(node, [spelling])
|
|
298
|
+
return withParserRanges({kind: "BooleanLiteral", location, value: spelling == "true"}, {literal: location})
|
|
299
|
+
}
|
|
300
|
+
if (node.type == "string") return withParserRanges({kind: "StringLiteral", location, value: this.string(node)}, {literal: location})
|
|
301
|
+
if (node.type == "unary_expression" || node.type == "error_union_type") {
|
|
302
|
+
const parts = this.parts(node)
|
|
303
|
+
const operator = node.type == "unary_expression" ? this.field(node, "operator") : parts[0]
|
|
304
|
+
const operand = node.type == "unary_expression" ? this.field(node, "argument") : this.field(node, "ok")
|
|
305
|
+
|
|
306
|
+
this.shape(node, [operator, operand])
|
|
307
|
+
if (operator.type != "-" && operator.type != "!") this.fail(operator, "unary operator")
|
|
308
|
+
return this.unary(node, operator.type == "-" ? "Negate" : "Not", this.expression(operand), operator)
|
|
309
|
+
}
|
|
310
|
+
if (node.type == "binary_expression") {
|
|
311
|
+
const left = this.field(node, "left")
|
|
312
|
+
const right = this.field(node, "right")
|
|
313
|
+
const operator = this.field(node, "operator")
|
|
314
|
+
const intent = binaryOperations.get(operator.type)
|
|
315
|
+
|
|
316
|
+
this.shape(node, [left, operator, right])
|
|
317
|
+
if (!intent) this.fail(operator, "unsupported binary operator")
|
|
318
|
+
return this.binary(node, intent, this.expression(left), this.expression(right), operator)
|
|
319
|
+
}
|
|
320
|
+
if (node.type == "call_expression") {
|
|
321
|
+
const call = this.call(node)
|
|
322
|
+
|
|
323
|
+
if (call.name.type != "identifier") this.fail(call.name, "direct function call required")
|
|
324
|
+
const name = this.text(call.name)
|
|
325
|
+
|
|
326
|
+
if (name == "semantifold_integer_negate") {
|
|
327
|
+
if (call.arguments.length != 1) this.fail(node, "negation helper arity")
|
|
328
|
+
return this.unary(node, "Negate", this.expression(call.arguments[0]), call.name)
|
|
329
|
+
}
|
|
330
|
+
const intent = helperOperations.get(name)
|
|
331
|
+
|
|
332
|
+
if (intent) {
|
|
333
|
+
if (call.arguments.length != 2) this.fail(node, "operator helper arity")
|
|
334
|
+
return this.binary(node, intent, this.expression(call.arguments[0]), this.expression(call.arguments[1]), call.name)
|
|
335
|
+
}
|
|
336
|
+
if (name.startsWith("semantifold_")) this.fail(call.name, "support helper outside its exact semantic role")
|
|
337
|
+
const callee = this.identifier(call.name)
|
|
338
|
+
|
|
339
|
+
return withParserRanges({kind: "CallExpression", location, callee, arguments: call.arguments.map(argument => this.expression(argument))}, {callee: this.location(call.name)})
|
|
340
|
+
}
|
|
341
|
+
return this.fail(node)
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Adapts one typed binary operator.
|
|
346
|
+
* @param {CstNode} node - Whole expression node.
|
|
347
|
+
* @param {string} intent - Shared operator intent.
|
|
348
|
+
* @param {Expression} left - Left operand.
|
|
349
|
+
* @param {Expression} right - Right operand.
|
|
350
|
+
* @param {CstNode} operator - Source operator occurrence.
|
|
351
|
+
* @returns {Expression} Adapted semantic expression.
|
|
352
|
+
*/
|
|
353
|
+
binary(node, intent, left, right, operator) {
|
|
354
|
+
const expression = /** @type {Expression} */ (withAdaptedOperation(withParserRanges({kind: "BinaryExpression", location: this.location(node), left, right},
|
|
355
|
+
{operator: this.location(operator)}), /** @type {import("../semantic/operators.js").AdaptedOperation} */ (intent)))
|
|
356
|
+
|
|
357
|
+
this.operations.set(expression, {node: operator, spelling: this.text(operator)})
|
|
358
|
+
return expression
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Adapts one typed unary operator.
|
|
363
|
+
* @param {CstNode} node - Whole expression node.
|
|
364
|
+
* @param {"Negate" | "Not"} intent - Shared operator intent.
|
|
365
|
+
* @param {Expression} operand - Converted operand.
|
|
366
|
+
* @param {CstNode} operator - Source operator occurrence.
|
|
367
|
+
* @returns {Expression} Adapted semantic expression.
|
|
368
|
+
*/
|
|
369
|
+
unary(node, intent, operand, operator) {
|
|
370
|
+
const expression = /** @type {Expression} */ (withAdaptedOperation(withParserRanges({kind: "UnaryExpression", location: this.location(node), operand},
|
|
371
|
+
{operator: this.location(operator)}), intent))
|
|
372
|
+
|
|
373
|
+
this.operations.set(expression, {node: operator, spelling: this.text(operator)})
|
|
374
|
+
return expression
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Converts one supported statement.
|
|
379
|
+
* @param {CstNode} node - Statement node.
|
|
380
|
+
* @returns {Statement} Semantic statement.
|
|
381
|
+
*/
|
|
382
|
+
statement(node) {
|
|
383
|
+
const location = this.location(node)
|
|
384
|
+
const parts = this.parts(node)
|
|
385
|
+
|
|
386
|
+
if (node.type == "variable_declaration") {
|
|
387
|
+
if (["const", "var"].includes(parts[0]?.type)) {
|
|
388
|
+
const typeNode = node.children.find(({field}) => field == "type")?.node
|
|
389
|
+
|
|
390
|
+
if (!typeNode) return missingType("zig", "local", location)
|
|
391
|
+
const name = parts[1]
|
|
392
|
+
const value = parts[5]
|
|
393
|
+
|
|
394
|
+
this.shape(node, [parts[0], name, ":", typeNode, "=", value, ";"])
|
|
395
|
+
return withParserRanges({kind: "LocalDeclaration", location, name: this.identifier(name), type: /** @type {import("../semantic/types.js").TypeReference} */ (this.type(typeNode)), mutable: parts[0].type == "var",
|
|
396
|
+
initializer: this.expression(value)}, {name: this.location(name), operator: this.location(parts[4])})
|
|
397
|
+
}
|
|
398
|
+
const targetNode = parts[0]
|
|
399
|
+
const value = parts[2]
|
|
400
|
+
|
|
401
|
+
this.shape(node, [targetNode, "=", value, ";"])
|
|
402
|
+
const target = withParserRanges({kind: /** @type {const} */ ("IdentifierExpression"), location: this.location(targetNode), name: this.identifier(targetNode)}, {name: this.location(targetNode)})
|
|
403
|
+
|
|
404
|
+
return withParserRanges({kind: "AssignmentStatement", location, target, expression: this.expression(value)}, {operator: this.location(parts[1])})
|
|
405
|
+
}
|
|
406
|
+
if (node.type == "if_statement") return this.conditional(node)
|
|
407
|
+
if (node.type != "expression_statement") this.fail(node, "explicit statement required")
|
|
408
|
+
const expression = parts[0]
|
|
409
|
+
|
|
410
|
+
this.shape(node, [expression, ";"])
|
|
411
|
+
if (expression.type == "return_expression") {
|
|
412
|
+
const returned = this.parts(expression)
|
|
413
|
+
|
|
414
|
+
if (returned.length == 1) {
|
|
415
|
+
this.shape(expression, ["return"])
|
|
416
|
+
return {kind: "ReturnStatement", location}
|
|
417
|
+
}
|
|
418
|
+
this.shape(expression, ["return", returned[1]])
|
|
419
|
+
return {kind: "ReturnStatement", location, expression: this.expression(returned[1])}
|
|
420
|
+
}
|
|
421
|
+
if (expression.type != "call_expression") this.fail(expression, "direct void call statement required")
|
|
422
|
+
const call = this.call(expression)
|
|
423
|
+
const name = this.text(call.name)
|
|
424
|
+
|
|
425
|
+
if (/^semantifold_print_(?:integer|boolean|string)$/u.test(name)) {
|
|
426
|
+
if (call.name.type != "identifier" || call.arguments.length != 1) this.fail(expression, "exact print helper call required")
|
|
427
|
+
const statement = {kind: /** @type {const} */ ("PrintStatement"), location, expression: this.expression(call.arguments[0])}
|
|
428
|
+
|
|
429
|
+
this.prints.set(statement, {node: call.name, type: name.slice("semantifold_print_".length)})
|
|
430
|
+
return statement
|
|
431
|
+
}
|
|
432
|
+
const value = this.expression(expression)
|
|
433
|
+
|
|
434
|
+
if (value.kind != "CallExpression") this.fail(expression, "direct void call statement required")
|
|
435
|
+
return {kind: "ExpressionStatement", location, expression: value}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Converts one strict Boolean conditional.
|
|
440
|
+
* @param {CstNode} node - If-statement node.
|
|
441
|
+
* @returns {import("../semantic/types.js").IfStatement} Semantic conditional.
|
|
442
|
+
*/
|
|
443
|
+
conditional(node) {
|
|
444
|
+
const condition = this.field(node, "condition")
|
|
445
|
+
const body = this.field(node, "body")
|
|
446
|
+
const alternative = this.parts(node).find(child => child.type == "else_clause")
|
|
447
|
+
|
|
448
|
+
this.shape(node, ["if", "(", condition, ")", body, ...(alternative ? [alternative] : [])])
|
|
449
|
+
const statement = {kind: /** @type {const} */ ("IfStatement"), location: this.location(node), condition: this.expression(condition), consequent: this.blockExpression(body)}
|
|
450
|
+
|
|
451
|
+
if (!alternative) return statement
|
|
452
|
+
const branch = this.field(alternative, "alternative")
|
|
453
|
+
|
|
454
|
+
this.shape(alternative, ["else", branch])
|
|
455
|
+
if (branch.type != "labeled_statement") this.fail(branch, "braced else block required")
|
|
456
|
+
const block = this.parts(branch)[0]
|
|
457
|
+
|
|
458
|
+
this.shape(branch, [block])
|
|
459
|
+
return {...statement, alternate: this.block(block)}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Unwraps one braced conditional block expression.
|
|
464
|
+
* @param {CstNode} node - Block-expression node.
|
|
465
|
+
* @returns {import("../semantic/types.js").Block} Semantic block.
|
|
466
|
+
*/
|
|
467
|
+
blockExpression(node) {
|
|
468
|
+
if (node.type != "block_expression") this.fail(node, "braced conditional block required")
|
|
469
|
+
const block = this.parts(node)[0]
|
|
470
|
+
|
|
471
|
+
this.shape(node, [block])
|
|
472
|
+
return this.block(block)
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Converts one lexical block while consuming exact compiler scaffolds.
|
|
477
|
+
* @param {CstNode} node - Block node.
|
|
478
|
+
* @param {number} [skip] - Fixed leading generated statements to omit.
|
|
479
|
+
* @param {import("../semantic/types.js").Parameter[]} [parameters] - Parameters requiring scaffold checks.
|
|
480
|
+
* @returns {import("../semantic/types.js").Block} Semantic block.
|
|
481
|
+
*/
|
|
482
|
+
block(node, skip = 0, parameters = []) {
|
|
483
|
+
if (node.type != "block") this.fail(node, "braced block required")
|
|
484
|
+
const parts = this.parts(node)
|
|
485
|
+
|
|
486
|
+
this.shape(node, ["{", ...parts.slice(1, -1), "}"])
|
|
487
|
+
const sourceStatements = this.significant(node).slice(1 + skip, -1)
|
|
488
|
+
return {kind: "Block", location: this.location(node), statements: this.generated
|
|
489
|
+
? this.generatedStatements(sourceStatements, parameters)
|
|
490
|
+
: this.directStatements(sourceStatements, parameters)}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Converts the legacy direct statement shape when diagnosing a missing generated marker.
|
|
495
|
+
* @param {CstNode[]} sourceStatements - Ordered direct children.
|
|
496
|
+
* @param {import("../semantic/types.js").Parameter[]} parameters - Function parameters.
|
|
497
|
+
* @returns {Statement[]} Semantic statements.
|
|
498
|
+
*/
|
|
499
|
+
directStatements(sourceStatements, parameters) {
|
|
500
|
+
let cursor = 0
|
|
501
|
+
|
|
502
|
+
for (const parameter of parameters) {
|
|
503
|
+
const kind = sourceStatements[cursor] ? this.useScaffold(sourceStatements[cursor], parameter.name) : undefined
|
|
504
|
+
|
|
505
|
+
this.scaffolds.set(parameter, kind ?? "none")
|
|
506
|
+
if (kind) cursor += 1
|
|
507
|
+
}
|
|
508
|
+
/** @type {Statement[]} */
|
|
509
|
+
const statements = []
|
|
510
|
+
|
|
511
|
+
while (cursor < sourceStatements.length) {
|
|
512
|
+
const statement = this.statement(sourceStatements[cursor])
|
|
513
|
+
|
|
514
|
+
statements.push(statement)
|
|
515
|
+
cursor += 1
|
|
516
|
+
if (statement.kind == "LocalDeclaration") {
|
|
517
|
+
const kind = sourceStatements[cursor] ? this.useScaffold(sourceStatements[cursor], statement.name) : undefined
|
|
518
|
+
|
|
519
|
+
this.scaffolds.set(statement, kind ?? "none")
|
|
520
|
+
if (kind) cursor += 1
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return statements
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Reconstructs complete generated statement regions before canonical whole-CST verification.
|
|
528
|
+
* @param {CstNode[]} sourceStatements - Significant direct block children.
|
|
529
|
+
* @param {import("../semantic/types.js").Parameter[]} parameters - Function parameters.
|
|
530
|
+
* @returns {Statement[]} Reconstructed semantic statements.
|
|
531
|
+
*/
|
|
532
|
+
generatedStatements(sourceStatements, parameters) {
|
|
533
|
+
/** @type {Statement[]} */
|
|
534
|
+
const statements = []
|
|
535
|
+
let cursor = 0
|
|
536
|
+
|
|
537
|
+
for (const parameter of parameters) {
|
|
538
|
+
const kind = sourceStatements[cursor] ? this.useScaffold(sourceStatements[cursor], parameter.name) : undefined
|
|
539
|
+
|
|
540
|
+
this.scaffolds.set(parameter, kind ?? "none")
|
|
541
|
+
if (kind) cursor += 1
|
|
542
|
+
}
|
|
543
|
+
while (cursor < sourceStatements.length) {
|
|
544
|
+
const begin = sourceStatements[cursor++]
|
|
545
|
+
const match = begin.type == "comment" &&
|
|
546
|
+
/^\/\/ semantifold:ordered-expression:zig:v1 begin ([0-9]{6}) ([0-9a-f]{64})$/u.exec(this.marker(begin))
|
|
547
|
+
|
|
548
|
+
if (!match) this.fail(begin, "complete generated ordered statement region required")
|
|
549
|
+
const outerExpressions = this.temporaryExpressions
|
|
550
|
+
const outerNames = this.temporaryNames
|
|
551
|
+
|
|
552
|
+
this.temporaryExpressions = new Map()
|
|
553
|
+
this.temporaryNames = new Set()
|
|
554
|
+
while (cursor < sourceStatements.length && this.temporaryDeclaration(sourceStatements[cursor])) {
|
|
555
|
+
cursor = this.readTemporary(sourceStatements, cursor)
|
|
556
|
+
}
|
|
557
|
+
const consumer = sourceStatements[cursor++]
|
|
558
|
+
|
|
559
|
+
if (!consumer) this.fail(begin, "missing generated ordered consumer")
|
|
560
|
+
const statement = this.statement(consumer)
|
|
561
|
+
|
|
562
|
+
if (this.temporaryExpressions.size != 0) this.fail(consumer, "unconsumed generated ordered temporary")
|
|
563
|
+
if (statement.kind == "LocalDeclaration") {
|
|
564
|
+
const kind = sourceStatements[cursor] ? this.useScaffold(sourceStatements[cursor], statement.name) : undefined
|
|
565
|
+
|
|
566
|
+
this.scaffolds.set(statement, kind ?? "none")
|
|
567
|
+
if (kind) cursor += 1
|
|
568
|
+
}
|
|
569
|
+
const end = sourceStatements[cursor++]
|
|
570
|
+
|
|
571
|
+
if (!end || end.type != "comment" || this.marker(end) !=
|
|
572
|
+
`// semantifold:ordered-expression:zig:v1 end ${match[1]} ${match[2]}`) this.fail(end ?? begin, "generated ordered marker pair")
|
|
573
|
+
this.temporaryExpressions = outerExpressions
|
|
574
|
+
this.temporaryNames = outerNames
|
|
575
|
+
statements.push(statement)
|
|
576
|
+
}
|
|
577
|
+
return statements
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Identifies a reserved ordered temporary declaration at the current region level.
|
|
582
|
+
* @param {CstNode} node - Candidate direct child.
|
|
583
|
+
* @returns {boolean} Whether it declares a reserved ordered temporary.
|
|
584
|
+
*/
|
|
585
|
+
temporaryDeclaration(node) {
|
|
586
|
+
const parts = this.parts(node)
|
|
587
|
+
|
|
588
|
+
return node.type == "variable_declaration" && ["const", "var"].includes(parts[0]?.type) &&
|
|
589
|
+
parts[1]?.type == "identifier" && this.text(parts[1]).startsWith("semantifold_ordered_")
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Expands one typed ordered temporary and an optional conditional Boolean RHS.
|
|
594
|
+
* @param {CstNode[]} nodes - Current region children.
|
|
595
|
+
* @param {number} cursor - Temporary declaration position.
|
|
596
|
+
* @returns {number} Next unconsumed position.
|
|
597
|
+
*/
|
|
598
|
+
readTemporary(nodes, cursor) {
|
|
599
|
+
const node = nodes[cursor++]
|
|
600
|
+
const parts = this.parts(node)
|
|
601
|
+
const nameNode = parts[1]
|
|
602
|
+
const name = this.text(nameNode)
|
|
603
|
+
const typeNode = this.field(node, "type")
|
|
604
|
+
const value = parts[5]
|
|
605
|
+
|
|
606
|
+
this.shape(node, [parts[0], nameNode, ":", typeNode, "=", value, ";"])
|
|
607
|
+
this.type(typeNode)
|
|
608
|
+
if (!/^semantifold_ordered_[0-9]{6}$/u.test(name) || this.temporaryNames?.has(name)) {
|
|
609
|
+
this.fail(nameNode, "generated ordered temporary declaration")
|
|
610
|
+
}
|
|
611
|
+
this.temporaryNames?.add(name)
|
|
612
|
+
let expression = this.expression(value)
|
|
613
|
+
const branch = nodes[cursor]
|
|
614
|
+
|
|
615
|
+
if (branch && this.shortCircuitBranch(branch, name)) {
|
|
616
|
+
const condition = this.field(branch, "condition")
|
|
617
|
+
const body = this.field(branch, "body")
|
|
618
|
+
const conditionParts = this.parts(condition)
|
|
619
|
+
const isOr = conditionParts[0]?.type == "!"
|
|
620
|
+
const target = isOr ? this.field(condition, condition.type == "unary_expression" ? "argument" : "ok") : condition
|
|
621
|
+
|
|
622
|
+
this.shape(branch, ["if", "(", condition, ")", body])
|
|
623
|
+
if (target.type != "identifier" || this.text(target) != name) this.fail(target, "generated short-circuit temporary test")
|
|
624
|
+
if (isOr) this.shape(condition, [conditionParts[0], target])
|
|
625
|
+
if (isOr && conditionParts[0]?.type != "!") this.fail(conditionParts[0] ?? condition, "generated short-circuit negation")
|
|
626
|
+
if (body.type != "block_expression") this.fail(body, "generated short-circuit block")
|
|
627
|
+
const block = this.parts(body)[0]
|
|
628
|
+
|
|
629
|
+
this.shape(body, [block])
|
|
630
|
+
if (block.type != "block") this.fail(block, "generated short-circuit block")
|
|
631
|
+
const inside = this.significant(block).slice(1, -1)
|
|
632
|
+
let offset = 0
|
|
633
|
+
|
|
634
|
+
while (offset < inside.length && this.temporaryDeclaration(inside[offset])) offset = this.readTemporary(inside, offset)
|
|
635
|
+
if (offset != inside.length - 1) this.fail(block, "generated short-circuit final assignment")
|
|
636
|
+
const assignment = inside[offset]
|
|
637
|
+
const assignmentParts = this.parts(assignment)
|
|
638
|
+
const left = assignmentParts[0]
|
|
639
|
+
const right = assignmentParts[2]
|
|
640
|
+
|
|
641
|
+
this.shape(assignment, [left, "=", right, ";"])
|
|
642
|
+
if (left.type != "identifier" || this.text(left) != name) this.fail(left, "generated short-circuit result write")
|
|
643
|
+
if (right.type != "binary_expression") this.fail(right, "generated short-circuit result expression")
|
|
644
|
+
const repeated = this.field(right, "left")
|
|
645
|
+
const operand = this.field(right, "right")
|
|
646
|
+
const operator = this.field(right, "operator")
|
|
647
|
+
const spelling = isOr ? "or" : "and"
|
|
648
|
+
|
|
649
|
+
this.shape(right, [repeated, spelling, operand])
|
|
650
|
+
if (repeated.type != "identifier" || this.text(repeated) != name) this.fail(repeated, "generated short-circuit consumed value")
|
|
651
|
+
expression = this.binary(right, isOr ? "Or" : "And", expression, this.expression(operand), operator)
|
|
652
|
+
cursor += 1
|
|
653
|
+
}
|
|
654
|
+
this.temporaryExpressions?.set(name, expression)
|
|
655
|
+
return cursor
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Distinguishes an internal conditional RHS from the following semantic `if` consumer using only direct CST structure.
|
|
660
|
+
* @param {CstNode} node - Candidate conditional.
|
|
661
|
+
* @param {string} name - Ordered Boolean result name.
|
|
662
|
+
* @returns {boolean} Whether the node has the complete outer shape of a short-circuit update.
|
|
663
|
+
*/
|
|
664
|
+
shortCircuitBranch(node, name) {
|
|
665
|
+
if (node.type != "if_statement" || this.parts(node).some(child => child.type == "else_clause")) return false
|
|
666
|
+
const body = node.children.find(({field}) => field == "body")?.node
|
|
667
|
+
|
|
668
|
+
if (body?.type != "block_expression") return false
|
|
669
|
+
const block = this.parts(body)[0]
|
|
670
|
+
|
|
671
|
+
if (block?.type != "block") return false
|
|
672
|
+
const inside = this.significant(block).slice(1, -1)
|
|
673
|
+
const final = inside.at(-1)
|
|
674
|
+
const finalParts = final ? this.parts(final) : []
|
|
675
|
+
|
|
676
|
+
return inside.length > 0 && !inside.some(child => child.type == "comment") && final?.type == "variable_declaration" &&
|
|
677
|
+
finalParts[0]?.type == "identifier" && this.text(finalParts[0]) == name && finalParts[1]?.type == "="
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* Recognizes one exact generated unused or unmutated binding scaffold.
|
|
682
|
+
* @param {CstNode} node - Candidate statement node.
|
|
683
|
+
* @param {string} name - Binding name.
|
|
684
|
+
* @returns {"use" | "address" | undefined} Scaffold form when matched.
|
|
685
|
+
*/
|
|
686
|
+
useScaffold(node, name) {
|
|
687
|
+
const parts = this.parts(node)
|
|
688
|
+
|
|
689
|
+
if (node.type != "variable_declaration" || parts.length != 4 || parts[0].type != "identifier" || this.text(parts[0]) != "_" || parts[1].type != "=" || parts[3].type != ";") return undefined
|
|
690
|
+
if (parts[2].type == "identifier" && this.text(parts[2]) == name) return "use"
|
|
691
|
+
if (parts[2].type == "reference_expression") {
|
|
692
|
+
const reference = this.parts(parts[2])
|
|
693
|
+
|
|
694
|
+
if (reference.length == 2 && reference[0].type == "&" && reference[1].type == "identifier" && this.text(reference[1]) == name) return "address"
|
|
695
|
+
}
|
|
696
|
+
return undefined
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Converts one canonical top-level semantic function.
|
|
701
|
+
* @param {CstNode} node - Function declaration node.
|
|
702
|
+
* @returns {import("../semantic/types.js").FunctionDeclaration} Semantic function.
|
|
703
|
+
*/
|
|
704
|
+
function(node) {
|
|
705
|
+
const name = this.field(node, "name")
|
|
706
|
+
const parameters = this.parts(node).find(child => child.type == "parameters")
|
|
707
|
+
const returnType = this.field(node, "type")
|
|
708
|
+
const body = this.field(node, "body")
|
|
709
|
+
|
|
710
|
+
if (!parameters) this.fail(node, "parameter list required")
|
|
711
|
+
this.shape(node, ["fn", name, parameters, returnType, body])
|
|
712
|
+
const parameterParts = this.parts(parameters)
|
|
713
|
+
const declarations = parameterParts.slice(1, -1).filter((_part, index) => index % 2 == 0)
|
|
714
|
+
|
|
715
|
+
this.shape(parameters, ["(", ...declarations.flatMap((parameter, index) => index ? [",", parameter] : [parameter]), ")"])
|
|
716
|
+
const converted = declarations.map(parameter => {
|
|
717
|
+
if (parameter.type != "parameter") this.fail(parameter, "required typed parameter")
|
|
718
|
+
const parameterName = this.field(parameter, "name")
|
|
719
|
+
const type = this.field(parameter, "type")
|
|
720
|
+
|
|
721
|
+
this.shape(parameter, [parameterName, ":", type])
|
|
722
|
+
return withParserRanges({kind: /** @type {const} */ ("Parameter"), location: this.location(parameter), name: this.identifier(parameterName), type: /** @type {import("../semantic/types.js").TypeReference} */ (this.type(type))}, {name: this.location(parameterName)})
|
|
723
|
+
})
|
|
724
|
+
|
|
725
|
+
return withParserRanges({kind: "FunctionDeclaration", location: this.location(node), name: this.identifier(name), parameters: converted,
|
|
726
|
+
returnType: this.type(returnType, true), body: this.block(body, 0, converted)}, {name: this.location(name)})
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* Compares every node, token, and field against canonical generated support.
|
|
731
|
+
* @param {CstNode} actual - Source subtree.
|
|
732
|
+
* @param {CstNode} expected - Canonical subtree.
|
|
733
|
+
* @param {ZigReader} canonical - Reader owning the canonical source.
|
|
734
|
+
*/
|
|
735
|
+
compareTree(actual, expected, canonical) {
|
|
736
|
+
if (actual.type != expected.type || actual.named != expected.named || actual.extra != expected.extra) this.fail(actual, "modified Zig support node")
|
|
737
|
+
const left = this.significant(actual)
|
|
738
|
+
const right = canonical.significant(expected)
|
|
739
|
+
|
|
740
|
+
if (left.length != right.length) this.fail(actual, "modified Zig support children")
|
|
741
|
+
if (!left.length) {
|
|
742
|
+
const actualText = actual.type == "comment" && /semantifold:/u.test(this.text(actual)) ? this.marker(actual) : this.text(actual)
|
|
743
|
+
const expectedText = expected.type == "comment" && /semantifold:/u.test(canonical.text(expected)) ? canonical.marker(expected) : canonical.text(expected)
|
|
744
|
+
|
|
745
|
+
if (actualText != expectedText) this.fail(actual, "modified Zig support token")
|
|
746
|
+
}
|
|
747
|
+
left.forEach((child, index) => {
|
|
748
|
+
const expectedChild = right[index]
|
|
749
|
+
|
|
750
|
+
if (!expectedChild || actual.children.find(edge => edge.node === child)?.field != expected.children.find(edge => edge.node === expectedChild)?.field) {
|
|
751
|
+
this.fail(child, "modified Zig support field")
|
|
752
|
+
}
|
|
753
|
+
this.compareTree(child, expectedChild, canonical)
|
|
754
|
+
})
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* Resolves a validated expression's scalar result.
|
|
759
|
+
* @param {Expression} expression - Semantic expression.
|
|
760
|
+
* @param {Map<string, Scalar>} bindings - Visible binding types.
|
|
761
|
+
* @returns {Scalar} Scalar result name.
|
|
762
|
+
*/
|
|
763
|
+
expressionType(expression, bindings) {
|
|
764
|
+
if (expression.kind == "StringLiteral") return "string"
|
|
765
|
+
if (expression.kind == "IntegerLiteral") return "integer"
|
|
766
|
+
if (expression.kind == "BooleanLiteral") return "boolean"
|
|
767
|
+
if (expression.kind == "BinaryExpression" || expression.kind == "UnaryExpression") return expression.type
|
|
768
|
+
if (expression.kind != "CallExpression" && expression.kind != "IdentifierExpression") throw new Error("Validated Zig source exposed a collection expression.")
|
|
769
|
+
const type = expression.kind == "CallExpression" ? this.functions.get(expression.callee) : bindings.get(expression.name)
|
|
770
|
+
|
|
771
|
+
if (!type) throw new Error("Validated Zig expression lost its scalar binding.")
|
|
772
|
+
return type
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Requires each typed operation to retain the one exact Zig operator/helper spelling that implements it.
|
|
777
|
+
* @param {Expression} expression - Resolved scalar expression.
|
|
778
|
+
*/
|
|
779
|
+
validateOperationExpression(expression) {
|
|
780
|
+
if (expression.kind == "CallExpression") {
|
|
781
|
+
expression.arguments.forEach(argument => this.validateOperationExpression(argument))
|
|
782
|
+
return
|
|
783
|
+
}
|
|
784
|
+
if (expression.kind == "UnaryExpression") {
|
|
785
|
+
const identity = this.operations.get(expression)
|
|
786
|
+
const expected = operationSpellings.get(expression.operation)
|
|
787
|
+
|
|
788
|
+
if (!identity) throw new Error("Resolved Zig unary operation lost its parser identity.")
|
|
789
|
+
if (identity.spelling != expected) this.fail(identity.node, `exact ${expression.operation} Zig operator/helper required`)
|
|
790
|
+
this.validateOperationExpression(expression.operand)
|
|
791
|
+
return
|
|
792
|
+
}
|
|
793
|
+
if (expression.kind == "BinaryExpression") {
|
|
794
|
+
const identity = this.operations.get(expression)
|
|
795
|
+
const expected = operationSpellings.get(expression.operation)
|
|
796
|
+
|
|
797
|
+
if (!identity) throw new Error("Resolved Zig binary operation lost its parser identity.")
|
|
798
|
+
if (identity.spelling != expected) this.fail(identity.node, `exact ${expression.operation} Zig operator/helper required`)
|
|
799
|
+
this.validateOperationExpression(expression.left)
|
|
800
|
+
this.validateOperationExpression(expression.right)
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* Validates operation identity throughout one lexical block.
|
|
806
|
+
* @param {import("../semantic/types.js").Block} block - Block whose expressions must retain exact Zig identity.
|
|
807
|
+
*/
|
|
808
|
+
validateOperations(block) {
|
|
809
|
+
for (const statement of block.statements) {
|
|
810
|
+
if (statement.kind == "LocalDeclaration") this.validateOperationExpression(statement.initializer)
|
|
811
|
+
else if (statement.kind == "AssignmentStatement" || statement.kind == "ExpressionStatement" || statement.kind == "PrintStatement") {
|
|
812
|
+
this.validateOperationExpression(statement.expression)
|
|
813
|
+
} else if (statement.kind == "ReturnStatement") {
|
|
814
|
+
if (statement.expression) this.validateOperationExpression(statement.expression)
|
|
815
|
+
} else if (statement.kind == "IfStatement") {
|
|
816
|
+
this.validateOperationExpression(statement.condition)
|
|
817
|
+
this.validateOperations(statement.consequent)
|
|
818
|
+
if (statement.alternate) this.validateOperations(statement.alternate)
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
/**
|
|
824
|
+
* Confirms each generated output helper matches its semantic operand.
|
|
825
|
+
* @param {import("../semantic/types.js").Block} block - Block to inspect.
|
|
826
|
+
* @param {Map<string, Scalar>} inherited - Visible binding types.
|
|
827
|
+
*/
|
|
828
|
+
validatePrints(block, inherited) {
|
|
829
|
+
const bindings = new Map(inherited)
|
|
830
|
+
|
|
831
|
+
for (const statement of block.statements) {
|
|
832
|
+
if (statement.kind == "LocalDeclaration") {
|
|
833
|
+
bindings.set(statement.name, /** @type {import("../semantic/types.js").TypeReference} */ (statement.type).name)
|
|
834
|
+
} else if (statement.kind == "PrintStatement") {
|
|
835
|
+
const print = this.prints.get(statement)
|
|
836
|
+
|
|
837
|
+
if (!print || this.expressionType(statement.expression, bindings) != print.type) this.fail(print?.node ?? /** @type {CstNode} */ ({}), "print helper scalar mismatch")
|
|
838
|
+
} else if (statement.kind == "IfStatement") {
|
|
839
|
+
this.validatePrints(statement.consequent, bindings)
|
|
840
|
+
if (statement.alternate) this.validatePrints(statement.alternate, bindings)
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* Confirms all and only compiler-required binding scaffolds are present.
|
|
847
|
+
* @param {import("../semantic/types.js").FunctionDeclaration | import("../semantic/types.js").EntryPoint} owner - Function or entry owner.
|
|
848
|
+
*/
|
|
849
|
+
validateScaffolds(owner) {
|
|
850
|
+
const usage = collectZigBindingUsage(owner.body)
|
|
851
|
+
const parameters = owner.kind == "FunctionDeclaration" ? owner.parameters : []
|
|
852
|
+
|
|
853
|
+
for (const parameter of parameters) {
|
|
854
|
+
const expected = usage.reads.has(parameter.name) ? "none" : "use"
|
|
855
|
+
|
|
856
|
+
if (this.scaffolds.get(parameter) != expected) unsupportedSyntax("zig", `incorrect unused-parameter scaffold for '${parameter.name}'`, parameter.location)
|
|
857
|
+
}
|
|
858
|
+
/**
|
|
859
|
+
* Checks local scaffolds recursively.
|
|
860
|
+
* @param {import("../semantic/types.js").Block} block - Block to inspect.
|
|
861
|
+
*/
|
|
862
|
+
const visit = (block) => {
|
|
863
|
+
for (const statement of block.statements) {
|
|
864
|
+
if (statement.kind == "LocalDeclaration") {
|
|
865
|
+
const expected = statement.mutable && !usage.writes.has(statement.name) ? "address" :
|
|
866
|
+
!statement.mutable && !usage.reads.has(statement.name) ? "use" : "none"
|
|
867
|
+
|
|
868
|
+
if (this.scaffolds.get(statement) != expected) unsupportedSyntax("zig", `incorrect local binding scaffold for '${statement.name}'`, statement.location)
|
|
869
|
+
} else if (statement.kind == "IfStatement") {
|
|
870
|
+
visit(statement.consequent)
|
|
871
|
+
if (statement.alternate) visit(statement.alternate)
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
visit(owner.body)
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
/**
|
|
880
|
+
* Converts a complete canonical Zig project entry.
|
|
881
|
+
* @param {CstNode} root - Source-file root.
|
|
882
|
+
* @returns {import("../semantic/types.js").SemanticModule} Validated semantic module.
|
|
883
|
+
*/
|
|
884
|
+
module(root) {
|
|
885
|
+
this.validateTree(root)
|
|
886
|
+
if (root.type != "source_file") this.fail(root, "Zig source root required")
|
|
887
|
+
const canonical = new ZigReader(this.filename, zigRuntime)
|
|
888
|
+
const support = canonical.significant(parseZigCst(zigRuntime).root)
|
|
889
|
+
const nodes = this.significant(root)
|
|
890
|
+
|
|
891
|
+
if (nodes.length < support.length + 2) this.fail(root, "complete Zig runtime, ordered marker, and main shell required")
|
|
892
|
+
support.forEach((expected, index) => this.compareTree(nodes[index], expected, canonical))
|
|
893
|
+
const programMarker = nodes[support.length]
|
|
894
|
+
|
|
895
|
+
if (programMarker.type != "comment" || this.marker(programMarker) != "// semantifold:program:zig:v1") {
|
|
896
|
+
this.fail(programMarker, "exact generated Zig ordered-expression marker required")
|
|
897
|
+
}
|
|
898
|
+
this.generated = true
|
|
899
|
+
const definitions = nodes.slice(support.length + 1)
|
|
900
|
+
/** @type {CstNode | undefined} */
|
|
901
|
+
let main
|
|
902
|
+
/** @type {CstNode[]} */
|
|
903
|
+
const functions = []
|
|
904
|
+
|
|
905
|
+
for (const node of definitions) {
|
|
906
|
+
if (node.type != "function_declaration") this.fail(node, "top-level free functions only")
|
|
907
|
+
const name = this.text(this.field(node, "name"))
|
|
908
|
+
|
|
909
|
+
if (name == "main") {
|
|
910
|
+
if (main) this.fail(node, "duplicate main")
|
|
911
|
+
main = node
|
|
912
|
+
} else {
|
|
913
|
+
if (main) this.fail(node, "semantic function after main")
|
|
914
|
+
functions.push(node)
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
if (!main) this.fail(root, "canonical pub fn main() void required")
|
|
918
|
+
const mainParts = this.parts(main)
|
|
919
|
+
const mainName = this.field(main, "name")
|
|
920
|
+
const parameters = mainParts.find(child => child.type == "parameters")
|
|
921
|
+
const returnType = this.field(main, "type")
|
|
922
|
+
const body = this.field(main, "body")
|
|
923
|
+
|
|
924
|
+
if (!parameters) this.fail(main, "main parameters")
|
|
925
|
+
this.shape(main, ["pub", "fn", mainName, parameters, returnType, body])
|
|
926
|
+
if (this.text(mainName) != "main") this.fail(mainName, "main name")
|
|
927
|
+
this.shape(parameters, ["(", ")"])
|
|
928
|
+
this.type(returnType, true)
|
|
929
|
+
if (this.text(returnType) != "void") this.fail(returnType, "main return type")
|
|
930
|
+
const bodyParts = this.parts(body)
|
|
931
|
+
const deferNode = bodyParts[1]
|
|
932
|
+
const shellSource = "pub fn main() void {\n defer semantifold_arena.deinit();\n}\n"
|
|
933
|
+
const shellReader = new ZigReader(this.filename, shellSource)
|
|
934
|
+
const shellMain = shellReader.parts(parseZigCst(shellSource).root)[0]
|
|
935
|
+
const expectedDefer = shellReader.parts(shellReader.field(shellMain, "body"))[1]
|
|
936
|
+
|
|
937
|
+
if (!deferNode) this.fail(body, "arena lifetime defer required")
|
|
938
|
+
this.compareTree(deferNode, expectedDefer, shellReader)
|
|
939
|
+
const module = validateParsedModule({kind: "Module", location: this.location(root), functions: functions.map(node => this.function(node)),
|
|
940
|
+
entryPoint: {kind: "EntryPoint", location: this.location(main), body: this.block(body, 1)}}, "zig")
|
|
941
|
+
|
|
942
|
+
this.functions = new Map(module.functions.map(declaration => [declaration.name,
|
|
943
|
+
/** @type {import("../semantic/types.js").TypeReference} */ (declaration.returnType).name]))
|
|
944
|
+
for (const declaration of module.functions) this.validateOperations(declaration.body)
|
|
945
|
+
this.validateOperations(module.entryPoint.body)
|
|
946
|
+
for (const declaration of module.functions) this.validatePrints(declaration.body, new Map(declaration.parameters.map(parameter => [parameter.name,
|
|
947
|
+
/** @type {import("../semantic/types.js").TypeReference} */ (parameter.type).name])))
|
|
948
|
+
this.validatePrints(module.entryPoint.body, new Map())
|
|
949
|
+
for (const declaration of module.functions) this.validateScaffolds(declaration)
|
|
950
|
+
this.validateScaffolds(module.entryPoint)
|
|
951
|
+
const canonicalSource = generateZigProject({module}).artifacts[1]?.content
|
|
952
|
+
|
|
953
|
+
if (typeof canonicalSource != "string") throw new Error("Zig backend returned a nontext entry artifact.")
|
|
954
|
+
this.compareTree(root, parseZigCst(canonicalSource).root, new ZigReader(this.filename, canonicalSource))
|
|
955
|
+
return module
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
/**
|
|
960
|
+
* Parses only the qualified frozen Zig CST and rejects unsupported input before adaptation.
|
|
961
|
+
* @param {{filename: string, source: string}} input - Exact source request.
|
|
962
|
+
* @returns {import("../semantic/types.js").SemanticModule} Zig semantic module.
|
|
963
|
+
*/
|
|
964
|
+
export function parseZig({filename, source}) {
|
|
965
|
+
const coordinates = createCoordinateIndex(source)
|
|
966
|
+
const location = {filename, start: indexedPointAt(coordinates, 0), end: indexedPointAt(coordinates, source.length)}
|
|
967
|
+
|
|
968
|
+
if (!hasOnlyUnicodeScalars(source)) {
|
|
969
|
+
const offset = source.search(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/u)
|
|
970
|
+
|
|
971
|
+
throw new SemantifoldDiagnostic({code: "PARSE_ERROR", language: "zig", message: "Zig source contains a lone UTF-16 surrogate.",
|
|
972
|
+
location: {filename, start: indexedPointAt(coordinates, offset), end: indexedPointAt(coordinates, offset + 1)}})
|
|
973
|
+
}
|
|
974
|
+
if (source.length > maximumZigSourceLength) return unsupportedSyntax("zig", "source exceeds the 1000000 UTF-16 input limit", location)
|
|
975
|
+
let snapshot
|
|
976
|
+
|
|
977
|
+
try {
|
|
978
|
+
snapshot = parseZigCst(source)
|
|
979
|
+
} catch (error) {
|
|
980
|
+
throw new SemantifoldDiagnostic({code: "PARSE_ERROR", language: "zig", location,
|
|
981
|
+
cause: error instanceof Error ? error : undefined, message: "The qualified Zig parser could not produce a complete CST."})
|
|
982
|
+
}
|
|
983
|
+
const reader = new ZigReader(filename, source)
|
|
984
|
+
|
|
985
|
+
try {
|
|
986
|
+
return reader.module(snapshot.root)
|
|
987
|
+
} catch (error) {
|
|
988
|
+
if (reader.generated && error instanceof SemantifoldDiagnostic && error.language == "zig" &&
|
|
989
|
+
error.code != "PARSE_ERROR" && error.code != "UNSUPPORTED_SYNTAX") {
|
|
990
|
+
return unsupportedSyntax("zig", `invalid generated ordered scaffold: ${error.code}`, error.location ?? location)
|
|
991
|
+
}
|
|
992
|
+
throw error
|
|
993
|
+
}
|
|
994
|
+
}
|