semantifold 0.3.0 → 0.4.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 +58 -15
- package/build/index.d.ts +48 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +57 -8
- package/build/src/artifact-path.d.ts +23 -1
- package/build/src/artifact-path.d.ts.map +1 -1
- package/build/src/artifact-path.js +45 -3
- package/build/src/artifacts.d.ts.map +1 -1
- package/build/src/artifacts.js +66 -1
- package/build/src/backends/c-validation.d.ts.map +1 -1
- package/build/src/backends/c-validation.js +7 -3
- package/build/src/backends/cpp-validation.d.ts.map +1 -1
- package/build/src/backends/cpp-validation.js +7 -3
- package/build/src/backends/csharp.js +7 -1
- package/build/src/backends/dart-runtime.d.ts +2 -0
- package/build/src/backends/dart-runtime.d.ts.map +1 -0
- package/build/src/backends/dart-runtime.js +41 -0
- package/build/src/backends/dart.d.ts +20 -0
- package/build/src/backends/dart.d.ts.map +1 -0
- package/build/src/backends/dart.js +321 -0
- package/build/src/backends/effects.d.ts +60 -0
- package/build/src/backends/effects.d.ts.map +1 -0
- package/build/src/backends/effects.js +746 -0
- package/build/src/backends/go.js +18 -4
- package/build/src/backends/identifiers.d.ts +36 -4
- package/build/src/backends/identifiers.d.ts.map +1 -1
- package/build/src/backends/identifiers.js +154 -10
- package/build/src/backends/index.d.ts.map +1 -1
- package/build/src/backends/index.js +27 -4
- package/build/src/backends/ios.d.ts +68 -0
- package/build/src/backends/ios.d.ts.map +1 -0
- package/build/src/backends/ios.js +1503 -0
- package/build/src/backends/java.d.ts.map +1 -1
- package/build/src/backends/java.js +429 -36
- package/build/src/backends/javascript.d.ts +6 -0
- package/build/src/backends/javascript.d.ts.map +1 -1
- package/build/src/backends/javascript.js +380 -24
- package/build/src/backends/kotlin-runtime.d.ts +3 -0
- package/build/src/backends/kotlin-runtime.d.ts.map +1 -0
- package/build/src/backends/kotlin-runtime.js +29 -0
- package/build/src/backends/kotlin.d.ts +23 -0
- package/build/src/backends/kotlin.d.ts.map +1 -0
- package/build/src/backends/kotlin.js +136 -0
- 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/ordered-expressions.d.ts.map +1 -1
- package/build/src/backends/ordered-expressions.js +39 -7
- package/build/src/backends/php-stdlib-support.d.ts +19 -0
- package/build/src/backends/php-stdlib-support.d.ts.map +1 -0
- package/build/src/backends/php-stdlib-support.js +227 -0
- package/build/src/backends/php.d.ts.map +1 -1
- package/build/src/backends/php.js +448 -33
- package/build/src/backends/program.d.ts +81 -0
- package/build/src/backends/program.d.ts.map +1 -0
- package/build/src/backends/program.js +1308 -0
- package/build/src/backends/python.js +7 -1
- package/build/src/backends/ruby.d.ts.map +1 -1
- package/build/src/backends/ruby.js +310 -24
- package/build/src/backends/rust-runtime.d.ts +7 -0
- package/build/src/backends/rust-runtime.d.ts.map +1 -0
- package/build/src/backends/rust-runtime.js +32 -0
- package/build/src/backends/rust-validation.d.ts +9 -0
- package/build/src/backends/rust-validation.d.ts.map +1 -0
- package/build/src/backends/rust-validation.js +78 -0
- package/build/src/backends/rust.d.ts +24 -0
- package/build/src/backends/rust.d.ts.map +1 -0
- package/build/src/backends/rust.js +344 -0
- package/build/src/backends/scalars.d.ts +14 -6
- package/build/src/backends/scalars.d.ts.map +1 -1
- package/build/src/backends/scalars.js +167 -17
- package/build/src/backends/shared.d.ts +23 -4
- package/build/src/backends/shared.d.ts.map +1 -1
- package/build/src/backends/shared.js +2158 -62
- package/build/src/backends/stdlib-support.d.ts +14 -0
- package/build/src/backends/stdlib-support.d.ts.map +1 -0
- package/build/src/backends/stdlib-support.js +237 -0
- package/build/src/backends/swift-runtime.d.ts +3 -0
- package/build/src/backends/swift-runtime.d.ts.map +1 -0
- package/build/src/backends/swift-runtime.js +14 -0
- package/build/src/backends/swift.d.ts +8 -0
- package/build/src/backends/swift.d.ts.map +1 -0
- package/build/src/backends/swift.js +127 -0
- package/build/src/backends/typescript.d.ts.map +1 -1
- package/build/src/backends/typescript.js +344 -24
- package/build/src/backends/wasm.d.ts +43 -0
- package/build/src/backends/wasm.d.ts.map +1 -0
- package/build/src/backends/wasm.js +2164 -0
- package/build/src/backends/writer.d.ts +235 -4
- package/build/src/backends/writer.d.ts.map +1 -1
- package/build/src/backends/writer.js +468 -3
- package/build/src/diagnostic.d.ts +4 -3
- package/build/src/diagnostic.d.ts.map +1 -1
- package/build/src/diagnostic.js +5 -3
- package/build/src/frontends/c.d.ts.map +1 -1
- package/build/src/frontends/c.js +5 -3
- package/build/src/frontends/cpp.d.ts.map +1 -1
- package/build/src/frontends/cpp.js +5 -3
- package/build/src/frontends/csharp.d.ts.map +1 -1
- package/build/src/frontends/csharp.js +7 -3
- package/build/src/frontends/dart.d.ts +10 -0
- package/build/src/frontends/dart.d.ts.map +1 -0
- package/build/src/frontends/dart.js +913 -0
- package/build/src/frontends/index.d.ts +3 -1
- package/build/src/frontends/index.d.ts.map +1 -1
- package/build/src/frontends/index.js +15 -5
- package/build/src/frontends/java.d.ts +110 -1
- package/build/src/frontends/java.d.ts.map +1 -1
- package/build/src/frontends/java.js +1725 -70
- package/build/src/frontends/javascript-typescript.d.ts +69 -1
- package/build/src/frontends/javascript-typescript.d.ts.map +1 -1
- package/build/src/frontends/javascript-typescript.js +1825 -93
- package/build/src/frontends/kotlin.d.ts +10 -0
- package/build/src/frontends/kotlin.d.ts.map +1 -0
- package/build/src/frontends/kotlin.js +685 -0
- package/build/src/frontends/php.d.ts +118 -1
- package/build/src/frontends/php.d.ts.map +1 -1
- package/build/src/frontends/php.js +1583 -76
- package/build/src/frontends/program.d.ts +74 -0
- package/build/src/frontends/program.d.ts.map +1 -0
- package/build/src/frontends/program.js +989 -0
- package/build/src/frontends/ruby.d.ts +68 -1
- package/build/src/frontends/ruby.d.ts.map +1 -1
- package/build/src/frontends/ruby.js +1446 -101
- package/build/src/frontends/rust-parser.d.ts +11 -0
- package/build/src/frontends/rust-parser.d.ts.map +1 -0
- package/build/src/frontends/rust-parser.js +15 -0
- package/build/src/frontends/rust.d.ts +14 -0
- package/build/src/frontends/rust.d.ts.map +1 -0
- package/build/src/frontends/rust.js +715 -0
- package/build/src/frontends/scalars.d.ts +10 -0
- package/build/src/frontends/scalars.d.ts.map +1 -1
- package/build/src/frontends/scalars.js +28 -2
- package/build/src/frontends/swift.d.ts +10 -0
- package/build/src/frontends/swift.d.ts.map +1 -0
- package/build/src/frontends/swift.js +860 -0
- package/build/src/frontends/types.d.ts +186 -0
- package/build/src/frontends/types.d.ts.map +1 -0
- package/build/src/frontends/types.js +634 -0
- package/build/src/language-registry.d.ts +46 -4
- package/build/src/language-registry.d.ts.map +1 -1
- package/build/src/language-registry.js +167 -13
- package/build/src/mapping.d.ts +2 -2
- package/build/src/mapping.d.ts.map +1 -1
- package/build/src/mapping.js +31 -10
- package/build/src/semantic/capabilities.d.ts +66 -0
- package/build/src/semantic/capabilities.d.ts.map +1 -0
- package/build/src/semantic/capabilities.js +649 -0
- package/build/src/semantic/effects.d.ts +26 -0
- package/build/src/semantic/effects.d.ts.map +1 -0
- package/build/src/semantic/effects.js +764 -0
- package/build/src/semantic/generics.d.ts +23 -0
- package/build/src/semantic/generics.d.ts.map +1 -0
- package/build/src/semantic/generics.js +61 -0
- package/build/src/semantic/lifetime.d.ts +37 -0
- package/build/src/semantic/lifetime.d.ts.map +1 -0
- package/build/src/semantic/lifetime.js +527 -0
- package/build/src/semantic/provenance.d.ts +9 -2
- package/build/src/semantic/provenance.d.ts.map +1 -1
- package/build/src/semantic/provenance.js +462 -20
- package/build/src/semantic/scalars.d.ts +11 -0
- package/build/src/semantic/scalars.d.ts.map +1 -1
- package/build/src/semantic/scalars.js +16 -1
- package/build/src/semantic/stdlib.d.ts +254 -0
- package/build/src/semantic/stdlib.d.ts.map +1 -0
- package/build/src/semantic/stdlib.js +880 -0
- package/build/src/semantic/types.d.ts +2952 -202
- package/build/src/semantic/types.d.ts.map +1 -1
- package/build/src/semantic/types.js +688 -15
- package/build/src/semantic/validate.d.ts +85 -5
- package/build/src/semantic/validate.d.ts.map +1 -1
- package/build/src/semantic/validate.js +2576 -98
- package/build/src/stdlib-facades.d.ts +304 -0
- package/build/src/stdlib-facades.d.ts.map +1 -0
- package/build/src/stdlib-facades.js +1167 -0
- package/build/src/stdlib-negotiation.d.ts +69 -0
- package/build/src/stdlib-negotiation.d.ts.map +1 -0
- package/build/src/stdlib-negotiation.js +403 -0
- package/build/src/stdlib-providers.d.ts +72 -0
- package/build/src/stdlib-providers.d.ts.map +1 -0
- package/build/src/stdlib-providers.js +339 -0
- package/build/src/toolchains.d.ts +55 -1
- package/build/src/toolchains.d.ts.map +1 -1
- package/build/src/toolchains.js +15 -4
- package/docs/architecture.md +45 -13
- package/docs/dart.md +60 -0
- package/docs/goals.md +3 -3
- package/docs/ios.md +121 -0
- package/docs/kotlin.md +42 -0
- package/docs/language-support.md +231 -26
- package/docs/multi-file-modules.md +52 -0
- package/docs/parser-qualification.md +121 -8
- package/docs/plans/2026-09-05-standard-library-portability.md +10 -10
- package/docs/rust.md +88 -0
- package/docs/source-maps.md +15 -7
- package/docs/standard-library-portability.md +39 -8
- package/docs/swift.md +44 -0
- package/docs/testing.md +366 -4
- package/docs/webassembly.md +52 -0
- package/index.js +63 -8
- package/node_modules/semantifold-tree-sitter-legacy-internal/README.md +2 -2
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/LICENSE +21 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/README.md +45 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/binding.gyp +30 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/binding.cc +20 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/binding_test.js +9 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/index.d.ts +28 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/index.js +11 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/grammar.js +1630 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/package.json +64 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/darwin-arm64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/darwin-x64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/linux-arm64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/linux-x64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/win32-arm64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/win32-x64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/highlights.scm +159 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/injections.scm +9 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/tags.scm +60 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/grammar.json +9405 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/node-types.json +5399 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/parser.c +192569 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/scanner.c +393 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/alloc.h +54 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/array.h +290 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/parser.h +266 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/tree-sitter-rust.wasm +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/tree-sitter.json +53 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/package.json +3 -2
- package/node_modules/semantifold-tree-sitter-legacy-internal/src/c.js +5 -4
- package/package.json +5 -2
- package/src/artifact-path.js +47 -2
- package/src/artifacts.js +67 -0
- package/src/backends/c-validation.js +6 -1
- package/src/backends/cpp-validation.js +6 -1
- package/src/backends/csharp.js +3 -0
- package/src/backends/dart-runtime.js +41 -0
- package/src/backends/dart.js +326 -0
- package/src/backends/effects.js +705 -0
- package/src/backends/go.js +14 -2
- package/src/backends/identifiers.js +160 -9
- package/src/backends/index.js +27 -2
- package/src/backends/ios.js +1612 -0
- package/src/backends/java.js +454 -36
- package/src/backends/javascript.js +389 -23
- package/src/backends/kotlin-runtime.js +29 -0
- package/src/backends/kotlin.js +141 -0
- package/src/backends/native-validation.js +1 -1
- package/src/backends/ordered-expressions.js +40 -6
- package/src/backends/php-stdlib-support.js +239 -0
- package/src/backends/php.js +469 -32
- package/src/backends/program.js +1407 -0
- package/src/backends/python.js +3 -0
- package/src/backends/ruby.js +326 -23
- package/src/backends/rust-runtime.js +34 -0
- package/src/backends/rust-validation.js +75 -0
- package/src/backends/rust.js +335 -0
- package/src/backends/scalars.js +141 -16
- package/src/backends/shared.js +2304 -124
- package/src/backends/stdlib-support.js +234 -0
- package/src/backends/swift-runtime.js +14 -0
- package/src/backends/swift.js +132 -0
- package/src/backends/typescript.js +349 -23
- package/src/backends/wasm.js +2208 -0
- package/src/backends/writer.js +520 -2
- package/src/diagnostic.js +4 -2
- package/src/frontends/c.js +4 -2
- package/src/frontends/cpp.js +4 -2
- package/src/frontends/csharp.js +4 -2
- package/src/frontends/dart.js +963 -0
- package/src/frontends/index.js +14 -4
- package/src/frontends/java.js +1977 -69
- package/src/frontends/javascript-typescript.js +2046 -102
- package/src/frontends/kotlin.js +713 -0
- package/src/frontends/php.js +1725 -79
- package/src/frontends/program.js +1062 -0
- package/src/frontends/ruby.js +1624 -98
- package/src/frontends/rust-parser.js +18 -0
- package/src/frontends/rust.js +714 -0
- package/src/frontends/scalars.js +31 -1
- package/src/frontends/swift.js +893 -0
- package/src/frontends/types.js +750 -0
- package/src/language-registry.js +172 -12
- package/src/mapping.js +33 -9
- package/src/semantic/capabilities.js +671 -0
- package/src/semantic/effects.js +737 -0
- package/src/semantic/generics.js +58 -0
- package/src/semantic/lifetime.js +517 -0
- package/src/semantic/provenance.js +412 -19
- package/src/semantic/scalars.js +17 -0
- package/src/semantic/stdlib.js +921 -0
- package/src/semantic/types.js +765 -14
- package/src/semantic/validate.js +2934 -209
- package/src/stdlib-facades.js +1211 -0
- package/src/stdlib-negotiation.js +446 -0
- package/src/stdlib-providers.js +377 -0
- package/src/toolchains.js +16 -3
|
@@ -0,0 +1,714 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import {rustRuntime} from "../backends/rust-runtime.js"
|
|
4
|
+
import {maximumRustSourceLength} from "../backends/rust-validation.js"
|
|
5
|
+
import {isRustIdentifier} from "../backends/identifiers.js"
|
|
6
|
+
import {missingType, SemantifoldDiagnostic, unsupportedSyntax} from "../diagnostic.js"
|
|
7
|
+
import {createCoordinateIndex, indexedPointAt} from "../semantic/location.js"
|
|
8
|
+
import {withAdaptedOperation} from "../semantic/operators.js"
|
|
9
|
+
import {withParserRanges} from "../semantic/provenance.js"
|
|
10
|
+
import {hasOnlyUnicodeScalars} from "../semantic/scalars.js"
|
|
11
|
+
import {validateParsedModule} from "../semantic/validate.js"
|
|
12
|
+
import {sourceScalarType} from "./scalars.js"
|
|
13
|
+
import {parseRustCst} from "./rust-parser.js"
|
|
14
|
+
|
|
15
|
+
/** @typedef {import("./rust-parser.js").CstNode} CstNode */
|
|
16
|
+
/** @typedef {import("../semantic/types.js").Expression} Expression */
|
|
17
|
+
/** @typedef {import("../semantic/types.js").Statement} Statement */
|
|
18
|
+
/** @typedef {import("../semantic/types.js").SemanticTypeName} Scalar */
|
|
19
|
+
|
|
20
|
+
const binaryOperations = new Map([
|
|
21
|
+
["+", "Add"], ["-", "Subtract"], ["*", "Multiply"], ["==", "Equal"], ["!=", "NotEqual"],
|
|
22
|
+
["<", "LessThan"], ["<=", "LessThanOrEqual"], [">", "GreaterThan"], [">=", "GreaterThanOrEqual"],
|
|
23
|
+
["&&", "And"], ["||", "Or"]
|
|
24
|
+
])
|
|
25
|
+
const arithmeticHelpers = new Map([
|
|
26
|
+
["semantifold_integer_add", "PhpAdd"], ["semantifold_integer_subtract", "Subtract"],
|
|
27
|
+
["semantifold_integer_multiply", "Multiply"]
|
|
28
|
+
])
|
|
29
|
+
|
|
30
|
+
/** Consumes complete Rust CST shapes and retains source-only scaffold constraints. */
|
|
31
|
+
class RustReader {
|
|
32
|
+
/**
|
|
33
|
+
* Owns source text and parser-origin metadata, never native parser handles.
|
|
34
|
+
* @param {string} filename - Original filename.
|
|
35
|
+
* @param {string} source - Exact source.
|
|
36
|
+
*/
|
|
37
|
+
constructor(filename, source) {
|
|
38
|
+
this.filename = filename
|
|
39
|
+
this.source = source
|
|
40
|
+
this.coordinates = createCoordinateIndex(source)
|
|
41
|
+
/** @type {Set<string>} */
|
|
42
|
+
this.helpers = new Set()
|
|
43
|
+
/** @type {Map<Expression, CstNode>} */
|
|
44
|
+
this.clones = new Map()
|
|
45
|
+
/** @type {Map<Expression, {borrowed: boolean, node: CstNode}>} */
|
|
46
|
+
this.additions = new Map()
|
|
47
|
+
/** @type {Map<Statement, {type: string, node: CstNode}>} */
|
|
48
|
+
this.prints = new Map()
|
|
49
|
+
/** @type {Map<string, Scalar>} */
|
|
50
|
+
this.functions = new Map()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Locates an original parser node in UTF-16 coordinates.
|
|
55
|
+
* @param {CstNode} node - Original node.
|
|
56
|
+
* @returns {import("../semantic/types.js").SourceLocation} Exact location.
|
|
57
|
+
*/
|
|
58
|
+
location(node) {
|
|
59
|
+
return {filename: this.filename, start: indexedPointAt(this.coordinates, node.startIndex), end: indexedPointAt(this.coordinates, node.endIndex)}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Reads the spelling of an already recognized parser node.
|
|
64
|
+
* @param {CstNode} node - Recognized node.
|
|
65
|
+
* @returns {string} Original spelling.
|
|
66
|
+
*/
|
|
67
|
+
text(node) {
|
|
68
|
+
return this.source.slice(node.startIndex, node.endIndex)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Rejects one located source-profile violation.
|
|
73
|
+
* @param {CstNode} node - Violating node.
|
|
74
|
+
* @param {string} [detail] - Profile detail.
|
|
75
|
+
* @returns {never} Always throws.
|
|
76
|
+
*/
|
|
77
|
+
fail(node, detail = node.type) {
|
|
78
|
+
unsupportedSyntax("rust", detail, this.location(node))
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Accounts for recovery, every extra, and doc comments before interpreting meaning.
|
|
83
|
+
* @param {CstNode} node - Current node.
|
|
84
|
+
* @param {number} [depth] - Bounded traversal depth.
|
|
85
|
+
* @returns {void}
|
|
86
|
+
*/
|
|
87
|
+
validateTree(node, depth = 0) {
|
|
88
|
+
if (depth > 512) this.fail(node, "Rust CST exceeds the 512-level traversal limit")
|
|
89
|
+
for (const {node: child} of node.children) this.validateTree(child, depth + 1)
|
|
90
|
+
if (node.error || node.missing || node.hasError) {
|
|
91
|
+
throw new SemantifoldDiagnostic({code: "PARSE_ERROR", language: "rust", location: this.location(node), message: `Rust parser recovery at '${node.type}'.`})
|
|
92
|
+
}
|
|
93
|
+
if (node.extra && !["line_comment", "block_comment"].includes(node.type)) this.fail(node, "unexpected extra parser child")
|
|
94
|
+
if (["line_comment", "block_comment"].includes(node.type) && /^(?:\/\/[!/]|\/\*[*!])/u.test(this.text(node))) {
|
|
95
|
+
this.fail(node, "doc-comment attribute")
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Omits only inert comments already checked during exhaustive traversal.
|
|
101
|
+
* @param {CstNode} node - Parent.
|
|
102
|
+
* @returns {CstNode[]} Ordered structural children.
|
|
103
|
+
*/
|
|
104
|
+
parts(node) {
|
|
105
|
+
return node.children.map(({node: child}) => child).filter(child => !["line_comment", "block_comment"].includes(child.type))
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Requires an exact sole parser field.
|
|
110
|
+
* @param {CstNode} node - Field owner.
|
|
111
|
+
* @param {string} name - Field name.
|
|
112
|
+
* @returns {CstNode} Sole field value.
|
|
113
|
+
*/
|
|
114
|
+
field(node, name) {
|
|
115
|
+
const edges = node.children.filter(({field}) => field == name)
|
|
116
|
+
|
|
117
|
+
if (edges.length != 1) this.fail(node, `expected one '${name}' field`)
|
|
118
|
+
return edges[0].node
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Requires all ordered children, including anonymous punctuation.
|
|
123
|
+
* @param {CstNode} node - Shape owner.
|
|
124
|
+
* @param {(CstNode | string)[]} expected - Exact child sequence.
|
|
125
|
+
* @returns {void}
|
|
126
|
+
*/
|
|
127
|
+
shape(node, expected) {
|
|
128
|
+
const parts = this.parts(node)
|
|
129
|
+
|
|
130
|
+
if (parts.length != expected.length || parts.some((child, index) =>
|
|
131
|
+
typeof expected[index] == "string" ? child.type != expected[index] || !child.named && this.text(child) != expected[index] : child !== expected[index])) this.fail(node, `${node.type} child shape`)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Protects Rust keywords and the private support namespace.
|
|
136
|
+
* @param {CstNode} node - Identifier token.
|
|
137
|
+
* @returns {string} Validated name.
|
|
138
|
+
*/
|
|
139
|
+
identifier(node) {
|
|
140
|
+
const name = this.text(node)
|
|
141
|
+
|
|
142
|
+
if (node.type != "identifier" || !isRustIdentifier(name)) this.fail(node, "reserved or unsupported identifier")
|
|
143
|
+
return name
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Accepts only explicitly spelled i64, bool and owned String.
|
|
148
|
+
* @param {CstNode} node - Type token.
|
|
149
|
+
* @returns {import("../semantic/types.js").TypeReference} Semantic scalar type.
|
|
150
|
+
*/
|
|
151
|
+
type(node) {
|
|
152
|
+
if (node.children.length || !["primitive_type", "type_identifier"].includes(node.type)) this.fail(node, "scalar type shape")
|
|
153
|
+
const spelling = this.text(node)
|
|
154
|
+
const type = sourceScalarType("rust", spelling, this.location(node))
|
|
155
|
+
|
|
156
|
+
if (!type) return missingType("rust", "scalar", this.location(node))
|
|
157
|
+
if ((spelling == "String") != (node.type == "type_identifier")) this.fail(node, "scalar type spelling")
|
|
158
|
+
return type
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Converts exact call syntax without interpreting macro token trees as calls.
|
|
163
|
+
* @param {CstNode} node - Call expression.
|
|
164
|
+
* @returns {{name: CstNode, arguments: CstNode[]}} Parsed direct argument sequence.
|
|
165
|
+
*/
|
|
166
|
+
call(node) {
|
|
167
|
+
if (node.type != "call_expression") this.fail(node, "direct call required")
|
|
168
|
+
const name = this.field(node, "function")
|
|
169
|
+
const list = this.field(node, "arguments")
|
|
170
|
+
|
|
171
|
+
this.shape(node, [name, list])
|
|
172
|
+
if (list.type != "arguments") this.fail(list, "call arguments")
|
|
173
|
+
const args = this.parts(list).slice(1, -1).filter((_part, index) => index % 2 == 0)
|
|
174
|
+
|
|
175
|
+
this.shape(list, ["(", ...args.flatMap((arg, index) => index == 0 ? [arg] : [",", arg]), ")"])
|
|
176
|
+
return {arguments: args, name}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Decodes parser-owned ordinary string content and exact scalar escapes.
|
|
181
|
+
* @param {CstNode} node - String literal.
|
|
182
|
+
* @returns {string} Unicode scalar value.
|
|
183
|
+
*/
|
|
184
|
+
string(node) {
|
|
185
|
+
if (node.type != "string_literal") this.fail(node, "ordinary String literal required")
|
|
186
|
+
const parts = this.parts(node)
|
|
187
|
+
|
|
188
|
+
this.shape(node, ['"', ...parts.slice(1, -1), '"'])
|
|
189
|
+
let value = ""
|
|
190
|
+
|
|
191
|
+
for (const part of parts.slice(1, -1)) {
|
|
192
|
+
const spelling = this.text(part)
|
|
193
|
+
|
|
194
|
+
if (part.type == "string_content") {
|
|
195
|
+
if (/[\r\n]/u.test(spelling)) this.fail(part, "raw string line terminator")
|
|
196
|
+
value += spelling
|
|
197
|
+
} else if (part.type == "escape_sequence") {
|
|
198
|
+
const escape = spelling.slice(1)
|
|
199
|
+
const simple = new Map([["0", "\0"], ["n", "\n"], ["r", "\r"], ["t", "\t"], ["\\", "\\"], ['"', '"'], ["'", "'"]])
|
|
200
|
+
|
|
201
|
+
if (spelling[0] != "\\") this.fail(part, "escape prefix")
|
|
202
|
+
if (simple.has(escape)) value += simple.get(escape)
|
|
203
|
+
else if (/^x[0-9a-fA-F]{2}$/u.test(escape)) {
|
|
204
|
+
const scalar = Number.parseInt(escape.slice(1), 16)
|
|
205
|
+
|
|
206
|
+
if (scalar > 127) this.fail(part, "non-ASCII byte escape")
|
|
207
|
+
value += String.fromCodePoint(scalar)
|
|
208
|
+
} else if (/^u\{[0-9a-fA-F]{1,6}\}$/u.test(escape)) {
|
|
209
|
+
const scalar = Number.parseInt(escape.slice(2, -1), 16)
|
|
210
|
+
|
|
211
|
+
if (scalar > 0x10FFFF || scalar >= 0xD800 && scalar <= 0xDFFF) this.fail(part, "non-scalar Unicode escape")
|
|
212
|
+
value += String.fromCodePoint(scalar)
|
|
213
|
+
} else this.fail(part, "unsupported string escape")
|
|
214
|
+
} else this.fail(part, "string child")
|
|
215
|
+
}
|
|
216
|
+
return value
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Converts exact scalar expressions while retaining source clone/borrow constraints.
|
|
221
|
+
* @param {CstNode} node - Expression node.
|
|
222
|
+
* @returns {Expression} Pending typed semantic expression.
|
|
223
|
+
*/
|
|
224
|
+
expression(node) {
|
|
225
|
+
const location = this.location(node)
|
|
226
|
+
|
|
227
|
+
if (node.type == "parenthesized_expression") {
|
|
228
|
+
const parts = this.parts(node)
|
|
229
|
+
|
|
230
|
+
this.shape(node, ["(", parts[1], ")"])
|
|
231
|
+
return this.expression(parts[1])
|
|
232
|
+
}
|
|
233
|
+
if (node.type == "identifier") return withParserRanges({kind: "IdentifierExpression", location, name: this.identifier(node)}, {name: location})
|
|
234
|
+
if (node.type == "integer_literal") {
|
|
235
|
+
const spelling = this.text(node)
|
|
236
|
+
const value = Number(spelling.slice(0, -3))
|
|
237
|
+
|
|
238
|
+
if (!/^(?:0|[1-9][0-9]*)i64$/u.test(spelling) || !Number.isSafeInteger(value)) this.fail(node, "non-safe or noncanonical i64 literal")
|
|
239
|
+
return withParserRanges({kind: "IntegerLiteral", location, value}, {literal: location})
|
|
240
|
+
}
|
|
241
|
+
if (node.type == "boolean_literal") {
|
|
242
|
+
const spelling = this.text(node)
|
|
243
|
+
|
|
244
|
+
if (spelling != "true" && spelling != "false") this.fail(node, "Boolean literal spelling")
|
|
245
|
+
this.shape(node, [spelling])
|
|
246
|
+
return withParserRanges({kind: "BooleanLiteral", location, value: spelling == "true"}, {literal: location})
|
|
247
|
+
}
|
|
248
|
+
if (node.type == "unary_expression") {
|
|
249
|
+
const parts = this.parts(node)
|
|
250
|
+
|
|
251
|
+
this.shape(node, [parts[0], parts[1]])
|
|
252
|
+
if (!["-", "!"].includes(parts[0].type)) this.fail(parts[0], "unary operator")
|
|
253
|
+
return this.unary(node, parts[0].type == "-" ? "Negate" : "Not", this.expression(parts[1]), parts[0])
|
|
254
|
+
}
|
|
255
|
+
if (node.type == "binary_expression") {
|
|
256
|
+
const left = this.field(node, "left")
|
|
257
|
+
const right = this.field(node, "right")
|
|
258
|
+
const operator = this.field(node, "operator")
|
|
259
|
+
const intent = binaryOperations.get(operator.type)
|
|
260
|
+
|
|
261
|
+
this.shape(node, [left, operator, right])
|
|
262
|
+
if (!intent) this.fail(operator, "unsupported binary operator")
|
|
263
|
+
const borrowed = operator.type == "+" && right.type == "reference_expression"
|
|
264
|
+
const value = borrowed ? this.field(right, "value") : right
|
|
265
|
+
|
|
266
|
+
if (borrowed) this.shape(right, ["&", value])
|
|
267
|
+
const expression = this.binary(node, intent, this.expression(left), this.expression(value), operator)
|
|
268
|
+
|
|
269
|
+
if (operator.type == "+") this.additions.set(expression, {borrowed, node: right})
|
|
270
|
+
return expression
|
|
271
|
+
}
|
|
272
|
+
if (node.type == "call_expression") {
|
|
273
|
+
const call = this.call(node)
|
|
274
|
+
const name = this.text(call.name)
|
|
275
|
+
|
|
276
|
+
if (call.name.type == "scoped_identifier") {
|
|
277
|
+
const owner = this.field(call.name, "path")
|
|
278
|
+
const method = this.field(call.name, "name")
|
|
279
|
+
|
|
280
|
+
this.shape(call.name, [owner, "::", method])
|
|
281
|
+
if (owner.type != "identifier" || this.text(owner) != "String" || method.type != "identifier" || this.text(method) != "from" || call.arguments.length != 1) {
|
|
282
|
+
this.fail(call.name, "exact String::from construction required")
|
|
283
|
+
}
|
|
284
|
+
return withParserRanges({kind: "StringLiteral", location, value: this.string(call.arguments[0])}, {literal: this.location(call.arguments[0])})
|
|
285
|
+
}
|
|
286
|
+
if (call.name.type == "field_expression") {
|
|
287
|
+
const receiver = this.field(call.name, "value")
|
|
288
|
+
const method = this.field(call.name, "field")
|
|
289
|
+
|
|
290
|
+
this.shape(call.name, [receiver, ".", method])
|
|
291
|
+
if (method.type != "field_identifier" || this.text(method) != "clone" || call.arguments.length) this.fail(call.name, "exact owned String clone required")
|
|
292
|
+
const expression = this.expression(receiver)
|
|
293
|
+
|
|
294
|
+
this.clones.set(expression, call.name)
|
|
295
|
+
return expression
|
|
296
|
+
}
|
|
297
|
+
if (call.name.type != "identifier") this.fail(call.name, "direct function call required")
|
|
298
|
+
if (this.helpers.has(name) && name == "semantifold_integer_negate") {
|
|
299
|
+
if (call.arguments.length != 1) this.fail(node, "negation helper arity")
|
|
300
|
+
return this.unary(node, "Negate", this.expression(call.arguments[0]), call.name)
|
|
301
|
+
}
|
|
302
|
+
const intent = this.helpers.has(name) ? arithmeticHelpers.get(name) : undefined
|
|
303
|
+
|
|
304
|
+
if (intent) {
|
|
305
|
+
if (call.arguments.length != 2) this.fail(node, "arithmetic helper arity")
|
|
306
|
+
return this.binary(node, intent, this.expression(call.arguments[0]), this.expression(call.arguments[1]), call.name)
|
|
307
|
+
}
|
|
308
|
+
const callee = this.identifier(call.name)
|
|
309
|
+
|
|
310
|
+
if (call.arguments.length != 2) this.fail(node, "semantic call arity")
|
|
311
|
+
return withParserRanges({kind: "CallExpression", location, callee, arguments: call.arguments.map(argument => this.expression(argument))}, {callee: this.location(call.name)})
|
|
312
|
+
}
|
|
313
|
+
return this.fail(node)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Retains parser operator intent until shared scalar resolution.
|
|
318
|
+
* @param {CstNode} node - Owning node.
|
|
319
|
+
* @param {string} intent - Parser operation.
|
|
320
|
+
* @param {Expression} left - Left operand.
|
|
321
|
+
* @param {Expression} right - Right operand.
|
|
322
|
+
* @param {CstNode} operator - Exact operator token.
|
|
323
|
+
* @returns {Expression} Pending binary expression.
|
|
324
|
+
*/
|
|
325
|
+
binary(node, intent, left, right, operator) {
|
|
326
|
+
return /** @type {Expression} */ (withAdaptedOperation(withParserRanges({kind: "BinaryExpression", location: this.location(node), left, right},
|
|
327
|
+
{operator: this.location(operator)}), /** @type {import("../semantic/operators.js").AdaptedOperation} */ (intent)))
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Retains unary parser intent and its original token range.
|
|
332
|
+
* @param {CstNode} node - Owning node.
|
|
333
|
+
* @param {"Negate" | "Not"} intent - Unary intent.
|
|
334
|
+
* @param {Expression} operand - Operand.
|
|
335
|
+
* @param {CstNode} operator - Exact token.
|
|
336
|
+
* @returns {Expression} Pending unary expression.
|
|
337
|
+
*/
|
|
338
|
+
unary(node, intent, operand, operator) {
|
|
339
|
+
return /** @type {Expression} */ (withAdaptedOperation(withParserRanges({kind: "UnaryExpression", location: this.location(node), operand},
|
|
340
|
+
{operator: this.location(operator)}), intent))
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Converts one explicitly typed initialized binding or executable statement.
|
|
345
|
+
* @param {CstNode} node - Statement.
|
|
346
|
+
* @returns {Statement} Semantic statement.
|
|
347
|
+
*/
|
|
348
|
+
statement(node) {
|
|
349
|
+
const location = this.location(node)
|
|
350
|
+
const parts = this.parts(node)
|
|
351
|
+
|
|
352
|
+
if (node.type == "let_declaration") {
|
|
353
|
+
const typeNode = node.children.find(({field}) => field == "type")?.node
|
|
354
|
+
|
|
355
|
+
if (!typeNode) return missingType("rust", "local", location)
|
|
356
|
+
const name = this.field(node, "pattern")
|
|
357
|
+
const value = this.field(node, "value")
|
|
358
|
+
const mutable = parts[1].type == "mutable_specifier"
|
|
359
|
+
|
|
360
|
+
this.shape(node, ["let", ...(mutable ? [parts[1]] : []), name, ":", typeNode, "=", value, ";"])
|
|
361
|
+
if (mutable && this.text(parts[1]) != "mut") this.fail(parts[1], "mutability token")
|
|
362
|
+
return withParserRanges({kind: "LocalDeclaration", location, name: this.identifier(name), type: this.type(typeNode), mutable,
|
|
363
|
+
initializer: this.expression(value)}, {name: this.location(name), operator: this.location(parts[parts.length - 3])})
|
|
364
|
+
}
|
|
365
|
+
if (node.type != "expression_statement") this.fail(node, "explicit statement required; tail expressions are unsupported")
|
|
366
|
+
const expression = parts[0]
|
|
367
|
+
|
|
368
|
+
if (expression.type == "if_expression") {
|
|
369
|
+
this.shape(node, [expression])
|
|
370
|
+
return this.conditional(expression)
|
|
371
|
+
}
|
|
372
|
+
this.shape(node, [expression, ";"])
|
|
373
|
+
if (expression.type == "return_expression") {
|
|
374
|
+
const returned = this.parts(expression)
|
|
375
|
+
|
|
376
|
+
this.shape(expression, ["return", returned[1]])
|
|
377
|
+
return {kind: "ReturnStatement", location, expression: this.expression(returned[1])}
|
|
378
|
+
}
|
|
379
|
+
if (expression.type == "assignment_expression") {
|
|
380
|
+
const left = this.field(expression, "left")
|
|
381
|
+
const right = this.field(expression, "right")
|
|
382
|
+
|
|
383
|
+
this.shape(expression, [left, "=", right])
|
|
384
|
+
const target = withParserRanges({kind: /** @type {const} */ ("IdentifierExpression"), location: this.location(left), name: this.identifier(left)}, {name: this.location(left)})
|
|
385
|
+
|
|
386
|
+
return withParserRanges({kind: "AssignmentStatement", location, target, expression: this.expression(right)}, {operator: this.location(this.parts(expression)[1])})
|
|
387
|
+
}
|
|
388
|
+
const call = this.call(expression)
|
|
389
|
+
const name = this.text(call.name)
|
|
390
|
+
|
|
391
|
+
if (call.name.type != "identifier" || !this.helpers.has(name) || !/^semantifold_print_(?:integer|boolean|string)$/u.test(name) || call.arguments.length != 1) this.fail(node, "exact print helper call required")
|
|
392
|
+
const statement = {kind: /** @type {const} */ ("PrintStatement"), location, expression: this.expression(call.arguments[0])}
|
|
393
|
+
|
|
394
|
+
this.prints.set(statement, {node: call.name, type: name.slice("semantifold_print_".length)})
|
|
395
|
+
return statement
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Converts exact braced conditionals and else-if nesting.
|
|
400
|
+
* @param {CstNode} node - If expression used as a statement.
|
|
401
|
+
* @returns {import("../semantic/types.js").IfStatement} Conditional statement.
|
|
402
|
+
*/
|
|
403
|
+
conditional(node) {
|
|
404
|
+
const condition = this.field(node, "condition")
|
|
405
|
+
const consequence = this.field(node, "consequence")
|
|
406
|
+
const alternative = node.children.find(({field}) => field == "alternative")?.node
|
|
407
|
+
|
|
408
|
+
this.shape(node, ["if", condition, consequence, ...(alternative ? [alternative] : [])])
|
|
409
|
+
const statement = {kind: /** @type {const} */ ("IfStatement"), location: this.location(node), condition: this.expression(condition), consequent: this.block(consequence)}
|
|
410
|
+
|
|
411
|
+
if (!alternative) return statement
|
|
412
|
+
const branch = this.parts(alternative)[1]
|
|
413
|
+
|
|
414
|
+
this.shape(alternative, ["else", branch])
|
|
415
|
+
return {...statement, alternate: branch.type == "if_expression" ? {kind: "Block", location: this.location(branch), statements: [this.conditional(branch)]} : this.block(branch)}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Converts one complete lexical block.
|
|
420
|
+
* @param {CstNode} node - Braced block.
|
|
421
|
+
* @returns {import("../semantic/types.js").Block} Semantic block.
|
|
422
|
+
*/
|
|
423
|
+
block(node) {
|
|
424
|
+
if (node.type != "block") this.fail(node, "braced block required")
|
|
425
|
+
const parts = this.parts(node)
|
|
426
|
+
|
|
427
|
+
this.shape(node, ["{", ...parts.slice(1, -1), "}"])
|
|
428
|
+
return {kind: "Block", location: this.location(node), statements: parts.slice(1, -1).map(child => this.statement(child))}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Converts a crate-root function with exactly two required by-value parameters.
|
|
433
|
+
* @param {CstNode} node - Function item.
|
|
434
|
+
* @returns {import("../semantic/types.js").FunctionDeclaration} Semantic declaration.
|
|
435
|
+
*/
|
|
436
|
+
function(node) {
|
|
437
|
+
const name = this.field(node, "name")
|
|
438
|
+
const parameters = this.field(node, "parameters")
|
|
439
|
+
const returnType = node.children.find(({field}) => field == "return_type")?.node
|
|
440
|
+
const body = this.field(node, "body")
|
|
441
|
+
|
|
442
|
+
if (!returnType) return missingType("rust", "function return", this.location(node))
|
|
443
|
+
this.shape(node, ["fn", name, parameters, "->", returnType, body])
|
|
444
|
+
this.shape(parameters, ["(", "parameter", ",", "parameter", ")"])
|
|
445
|
+
const converted = [this.parts(parameters)[1], this.parts(parameters)[3]].map(parameter => {
|
|
446
|
+
const pattern = this.field(parameter, "pattern")
|
|
447
|
+
const type = this.field(parameter, "type")
|
|
448
|
+
|
|
449
|
+
this.shape(parameter, [pattern, ":", type])
|
|
450
|
+
return withParserRanges({kind: /** @type {const} */ ("Parameter"), location: this.location(parameter), name: this.identifier(pattern), type: this.type(type)}, {name: this.location(pattern)})
|
|
451
|
+
})
|
|
452
|
+
|
|
453
|
+
return withParserRanges({kind: "FunctionDeclaration", location: this.location(node), name: this.identifier(name), parameters: converted,
|
|
454
|
+
returnType: this.type(returnType), body: this.block(body)}, {name: this.location(name)})
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Compares all fields, children and leaf spellings against qualified support syntax.
|
|
459
|
+
* @param {CstNode} actual - Original node.
|
|
460
|
+
* @param {CstNode} expected - Qualified node.
|
|
461
|
+
* @param {RustReader} canonical - Qualified spelling reader.
|
|
462
|
+
* @returns {void}
|
|
463
|
+
*/
|
|
464
|
+
compareTree(actual, expected, canonical) {
|
|
465
|
+
if (actual.type != expected.type || actual.named != expected.named || actual.extra != expected.extra) this.fail(actual, "modified Rust support node")
|
|
466
|
+
const left = this.parts(actual)
|
|
467
|
+
const right = canonical.parts(expected)
|
|
468
|
+
|
|
469
|
+
if (left.length != right.length) this.fail(actual, "modified Rust support children")
|
|
470
|
+
if (!left.length && this.text(actual) != canonical.text(expected)) this.fail(actual, "modified Rust support token")
|
|
471
|
+
left.forEach((child, index) => {
|
|
472
|
+
if (actual.children.find(edge => edge.node === child)?.field != expected.children.find(edge => edge.node === right[index])?.field) this.fail(child, "modified Rust support field")
|
|
473
|
+
this.compareTree(child, right[index], canonical)
|
|
474
|
+
})
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Resolves only already validated scalar expressions for scaffold checking.
|
|
479
|
+
* @param {Expression} expression - Resolved semantic expression.
|
|
480
|
+
* @param {Map<string, Scalar>} bindings - Lexical types.
|
|
481
|
+
* @returns {Scalar} Semantic result type.
|
|
482
|
+
*/
|
|
483
|
+
expressionType(expression, bindings) {
|
|
484
|
+
if (expression.kind == "StringLiteral") return "string"
|
|
485
|
+
if (expression.kind == "IntegerLiteral") return "integer"
|
|
486
|
+
if (expression.kind == "BooleanLiteral") return "boolean"
|
|
487
|
+
if (expression.kind == "BinaryExpression" || expression.kind == "UnaryExpression") return expression.type
|
|
488
|
+
if (expression.kind != "CallExpression" && expression.kind != "IdentifierExpression") {
|
|
489
|
+
throw new Error("Validated Rust source exposed a collection expression.")
|
|
490
|
+
}
|
|
491
|
+
const type = expression.kind == "CallExpression" ? this.functions.get(expression.callee) : bindings.get(expression.name)
|
|
492
|
+
|
|
493
|
+
if (!type) throw new Error("Validated Rust expression lost its scalar binding.")
|
|
494
|
+
return type
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Checks clone, borrow and print constraints after shared operation resolution.
|
|
499
|
+
* @param {import("../semantic/types.js").Block} block - Resolved block.
|
|
500
|
+
* @param {Map<string, Scalar>} inherited - Enclosing types.
|
|
501
|
+
* @returns {void}
|
|
502
|
+
*/
|
|
503
|
+
validateScalars(block, inherited) {
|
|
504
|
+
const bindings = new Map(inherited)
|
|
505
|
+
/**
|
|
506
|
+
* Checks each original clone and borrowed concatenation after scalar resolution.
|
|
507
|
+
* @param {Expression} expression - Current expression.
|
|
508
|
+
* @returns {void}
|
|
509
|
+
*/
|
|
510
|
+
const visit = expression => {
|
|
511
|
+
const clone = this.clones.get(expression)
|
|
512
|
+
const addition = this.additions.get(expression)
|
|
513
|
+
|
|
514
|
+
if (clone && this.expressionType(expression, bindings) != "string") this.fail(clone, "clone requires owned String")
|
|
515
|
+
if (addition && (this.expressionType(expression, bindings) == "string") != addition.borrowed) this.fail(addition.node, "string concatenation requires exactly a borrowed String RHS")
|
|
516
|
+
if (expression.kind == "CallExpression") expression.arguments.forEach(visit)
|
|
517
|
+
else if (expression.kind == "UnaryExpression") visit(expression.operand)
|
|
518
|
+
else if (expression.kind == "BinaryExpression") { visit(expression.left); visit(expression.right) }
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
for (const statement of block.statements) {
|
|
522
|
+
const expression = statement.kind == "IfStatement" ? statement.condition :
|
|
523
|
+
statement.kind == "LocalDeclaration" ? statement.initializer :
|
|
524
|
+
statement.kind == "AssignmentStatement" || statement.kind == "ExpressionStatement" || statement.kind == "PrintStatement" ? statement.expression :
|
|
525
|
+
statement.kind == "ReturnStatement" ? statement.expression : undefined
|
|
526
|
+
|
|
527
|
+
if (!expression) throw new Error("Validated Rust source contained a bare return.")
|
|
528
|
+
visit(expression)
|
|
529
|
+
if (statement.kind == "LocalDeclaration") bindings.set(statement.name,
|
|
530
|
+
/** @type {import("../semantic/types.js").TypeReference} */ (statement.type).name)
|
|
531
|
+
if (statement.kind == "PrintStatement") {
|
|
532
|
+
const print = this.prints.get(statement)
|
|
533
|
+
|
|
534
|
+
if (!print) throw new Error("Rust print lost its original helper identity.")
|
|
535
|
+
if (this.expressionType(expression, bindings) != print.type) this.fail(print.node, "print helper scalar mismatch")
|
|
536
|
+
}
|
|
537
|
+
if (statement.kind == "IfStatement") {
|
|
538
|
+
this.validateScalars(statement.consequent, bindings)
|
|
539
|
+
if (statement.alternate) this.validateScalars(statement.alternate, bindings)
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Validates genuine source moves in evaluation order, retaining implicit comparison borrows.
|
|
546
|
+
* @param {Expression} expression - Original expression occurrence.
|
|
547
|
+
* @param {Map<string, Scalar>} bindings - Validated lexical types.
|
|
548
|
+
* @param {Set<string>} moved - Definitely or possibly consumed String bindings.
|
|
549
|
+
* @param {Set<string>} borrowed - Borrows held by enclosing operand evaluation.
|
|
550
|
+
* @param {"move" | "borrow"} [usage] - How this expression's result is consumed.
|
|
551
|
+
* @param {boolean} [applyClone] - Whether to apply this occurrence's checked clone wrapper.
|
|
552
|
+
* @returns {Set<string>} Place borrows held until the containing operation completes.
|
|
553
|
+
*/
|
|
554
|
+
ownershipExpression(expression, bindings, moved, borrowed, usage = "move", applyClone = true) {
|
|
555
|
+
if (applyClone && this.clones.has(expression)) {
|
|
556
|
+
this.ownershipExpression(expression, bindings, moved, borrowed, "borrow", false)
|
|
557
|
+
return new Set()
|
|
558
|
+
}
|
|
559
|
+
if (expression.kind == "IdentifierExpression" && this.expressionType(expression, bindings) == "string") {
|
|
560
|
+
const name = expression.name
|
|
561
|
+
|
|
562
|
+
if (moved.has(name)) unsupportedSyntax("rust", `use of moved String '${name}'`, expression.location)
|
|
563
|
+
if (usage == "borrow") return new Set([name])
|
|
564
|
+
if (borrowed.has(name)) unsupportedSyntax("rust", `cannot move borrowed String '${name}' during operand evaluation`, expression.location)
|
|
565
|
+
moved.add(name)
|
|
566
|
+
} else if (expression.kind == "CallExpression") {
|
|
567
|
+
for (const argument of expression.arguments) this.ownershipExpression(argument, bindings, moved, borrowed)
|
|
568
|
+
} else if (expression.kind == "UnaryExpression") this.ownershipExpression(expression.operand, bindings, moved, borrowed)
|
|
569
|
+
else if (expression.kind == "BinaryExpression") {
|
|
570
|
+
if (expression.operation == "StringEqual" || expression.operation == "StringNotEqual") {
|
|
571
|
+
const held = this.ownershipExpression(expression.left, bindings, moved, borrowed, "borrow")
|
|
572
|
+
|
|
573
|
+
this.ownershipExpression(expression.right, bindings, moved, new Set([...borrowed, ...held]), "borrow")
|
|
574
|
+
} else {
|
|
575
|
+
this.ownershipExpression(expression.left, bindings, moved, borrowed)
|
|
576
|
+
if (expression.operation == "BooleanAnd" || expression.operation == "BooleanOr") {
|
|
577
|
+
const conditional = new Set(moved)
|
|
578
|
+
|
|
579
|
+
this.ownershipExpression(expression.right, bindings, conditional, borrowed)
|
|
580
|
+
for (const name of conditional) moved.add(name)
|
|
581
|
+
} else this.ownershipExpression(expression.right, bindings, moved, borrowed, expression.operation == "StringConcat" ? "borrow" : "move")
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
return new Set()
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Merges source availability only from paths that continue, restoring explicit assignments.
|
|
589
|
+
* @param {import("../semantic/types.js").Block} block - Original lexical block.
|
|
590
|
+
* @param {Map<string, Scalar>} inherited - Visible scalar bindings.
|
|
591
|
+
* @param {Set<string>} inheritedMoved - Incoming consumed bindings.
|
|
592
|
+
* @returns {{moved: Set<string>, returns: boolean}} Outgoing source availability and control flow.
|
|
593
|
+
*/
|
|
594
|
+
validateOwnership(block, inherited, inheritedMoved) {
|
|
595
|
+
const bindings = new Map(inherited)
|
|
596
|
+
const moved = new Set(inheritedMoved)
|
|
597
|
+
|
|
598
|
+
for (const statement of block.statements) {
|
|
599
|
+
const expression = statement.kind == "IfStatement" ? statement.condition :
|
|
600
|
+
statement.kind == "LocalDeclaration" ? statement.initializer :
|
|
601
|
+
statement.kind == "AssignmentStatement" || statement.kind == "ExpressionStatement" || statement.kind == "PrintStatement" ? statement.expression :
|
|
602
|
+
statement.kind == "ReturnStatement" ? statement.expression : undefined
|
|
603
|
+
|
|
604
|
+
if (!expression) throw new Error("Validated Rust source contained a bare return.")
|
|
605
|
+
this.ownershipExpression(expression, bindings, moved, new Set())
|
|
606
|
+
if (statement.kind == "ReturnStatement") return {moved, returns: true}
|
|
607
|
+
if (statement.kind == "LocalDeclaration") bindings.set(statement.name,
|
|
608
|
+
/** @type {import("../semantic/types.js").TypeReference} */ (statement.type).name)
|
|
609
|
+
else if (statement.kind == "AssignmentStatement") moved.delete(statement.target.name)
|
|
610
|
+
else if (statement.kind == "IfStatement") {
|
|
611
|
+
const consequent = this.validateOwnership(statement.consequent, bindings, moved)
|
|
612
|
+
const alternate = statement.alternate ? this.validateOwnership(statement.alternate, bindings, moved) : {moved: new Set(moved), returns: false}
|
|
613
|
+
|
|
614
|
+
if (consequent.returns && alternate.returns) return {moved, returns: true}
|
|
615
|
+
moved.clear()
|
|
616
|
+
for (const path of [consequent, alternate]) {
|
|
617
|
+
if (!path.returns) for (const name of path.moved) if (bindings.has(name)) moved.add(name)
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
return {moved, returns: false}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* Validates the complete crate and collapses only exact declared support.
|
|
626
|
+
* @param {CstNode} root - Complete root.
|
|
627
|
+
* @returns {import("../semantic/types.js").SemanticModule} Resolved semantics.
|
|
628
|
+
*/
|
|
629
|
+
module(root) {
|
|
630
|
+
this.validateTree(root)
|
|
631
|
+
if (root.type != "source_file") this.fail(root, "crate root required")
|
|
632
|
+
const canonical = new RustReader(this.filename, rustRuntime)
|
|
633
|
+
const support = canonical.parts(parseRustCst(rustRuntime).root)
|
|
634
|
+
const helpers = new Map(support.filter(node => node.type == "function_item").map(node => [canonical.text(canonical.field(node, "name")), node]))
|
|
635
|
+
/** @type {CstNode[]} */
|
|
636
|
+
const definitions = []
|
|
637
|
+
let lint = false
|
|
638
|
+
/** @type {CstNode | undefined} */
|
|
639
|
+
let main
|
|
640
|
+
|
|
641
|
+
for (const node of this.parts(root)) {
|
|
642
|
+
if (node.type == "inner_attribute_item") {
|
|
643
|
+
if (lint || definitions.length || main || this.helpers.size) this.fail(node, "misplaced Rust lint support")
|
|
644
|
+
this.compareTree(node, support[0], canonical)
|
|
645
|
+
lint = true
|
|
646
|
+
continue
|
|
647
|
+
}
|
|
648
|
+
if (node.type != "function_item") this.fail(node, "crate-root free functions only")
|
|
649
|
+
const name = this.text(this.field(node, "name"))
|
|
650
|
+
const helper = helpers.get(name)
|
|
651
|
+
|
|
652
|
+
if (helper) {
|
|
653
|
+
if (this.helpers.has(name)) this.fail(node, "duplicate Rust helper")
|
|
654
|
+
this.compareTree(node, helper, canonical)
|
|
655
|
+
this.helpers.add(name)
|
|
656
|
+
} else if (name == "main") {
|
|
657
|
+
if (main) this.fail(node, "duplicate main")
|
|
658
|
+
main = node
|
|
659
|
+
} else definitions.push(node)
|
|
660
|
+
}
|
|
661
|
+
if (!main) this.fail(root, "canonical fn main() required")
|
|
662
|
+
if (!definitions.length) this.fail(root, "semantic function required")
|
|
663
|
+
const name = this.field(main, "name")
|
|
664
|
+
const parameters = this.field(main, "parameters")
|
|
665
|
+
const body = this.field(main, "body")
|
|
666
|
+
|
|
667
|
+
this.shape(main, ["fn", name, parameters, body])
|
|
668
|
+
this.shape(parameters, ["(", ")"])
|
|
669
|
+
const module = validateParsedModule({kind: "Module", location: this.location(root), functions: definitions.map(node => this.function(node)),
|
|
670
|
+
entryPoint: {kind: "EntryPoint", location: this.location(main), body: this.block(body)}}, "rust")
|
|
671
|
+
|
|
672
|
+
this.functions = new Map(module.functions.map(declaration => [
|
|
673
|
+
declaration.name,
|
|
674
|
+
/** @type {Scalar} */ (/** @type {import("../semantic/types.js").TypeReference} */ (declaration.returnType).name)
|
|
675
|
+
]))
|
|
676
|
+
for (const declaration of module.functions) {
|
|
677
|
+
const bindings = new Map(declaration.parameters.map(parameter => [parameter.name,
|
|
678
|
+
/** @type {import("../semantic/types.js").TypeReference} */ (parameter.type).name]))
|
|
679
|
+
|
|
680
|
+
this.validateScalars(declaration.body, bindings)
|
|
681
|
+
this.validateOwnership(declaration.body, bindings, new Set())
|
|
682
|
+
}
|
|
683
|
+
this.validateScalars(module.entryPoint.body, new Map())
|
|
684
|
+
this.validateOwnership(module.entryPoint.body, new Map(), new Set())
|
|
685
|
+
return module
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Parses only the qualified frozen CST and rejects unsupported input before adaptation.
|
|
691
|
+
* @param {{filename: string, source: string}} input - Exact source request.
|
|
692
|
+
* @returns {import("../semantic/types.js").SemanticModule} Rust semantic module.
|
|
693
|
+
*/
|
|
694
|
+
export function parseRust({filename, source}) {
|
|
695
|
+
const coordinates = createCoordinateIndex(source)
|
|
696
|
+
const location = {filename, start: indexedPointAt(coordinates, 0), end: indexedPointAt(coordinates, source.length)}
|
|
697
|
+
|
|
698
|
+
if (!hasOnlyUnicodeScalars(source)) {
|
|
699
|
+
const offset = source.search(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/u)
|
|
700
|
+
|
|
701
|
+
throw new SemantifoldDiagnostic({code: "PARSE_ERROR", language: "rust", message: "Rust source contains a lone UTF-16 surrogate.",
|
|
702
|
+
location: {filename, start: indexedPointAt(coordinates, offset), end: indexedPointAt(coordinates, offset + 1)}})
|
|
703
|
+
}
|
|
704
|
+
if (source.length > maximumRustSourceLength) return unsupportedSyntax("rust", "source exceeds the frozen Rust parser's 32767 UTF-16 input limit", location)
|
|
705
|
+
let snapshot
|
|
706
|
+
|
|
707
|
+
try {
|
|
708
|
+
snapshot = parseRustCst(source)
|
|
709
|
+
} catch (error) {
|
|
710
|
+
throw new SemantifoldDiagnostic({code: "PARSE_ERROR", language: "rust", location,
|
|
711
|
+
cause: error instanceof Error ? error : undefined, message: "The qualified Rust parser could not produce a complete CST."})
|
|
712
|
+
}
|
|
713
|
+
return new RustReader(filename, source).module(snapshot.root)
|
|
714
|
+
}
|