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,963 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import Parser from "tree-sitter"
|
|
4
|
+
import DartLanguage from "tree-sitter-dart-orchard/bindings/node/index.js"
|
|
5
|
+
import {isDartIdentifier} from "../backends/identifiers.js"
|
|
6
|
+
import {dartRuntime} from "../backends/dart-runtime.js"
|
|
7
|
+
import {missingType, SemantifoldDiagnostic, unsupportedSyntax} from "../diagnostic.js"
|
|
8
|
+
import {locationFromOffsets, moduleLocation, utf8ByteOffsetToUtf16Offset} 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
|
+
|
|
15
|
+
const parser = new Parser()
|
|
16
|
+
|
|
17
|
+
parser.setLanguage(/** @type {import("tree-sitter").Language} */ (/** @type {unknown} */ (DartLanguage)))
|
|
18
|
+
|
|
19
|
+
const binaryOperations = new Map([
|
|
20
|
+
["+", "Add"], ["-", "Subtract"], ["*", "Multiply"], ["==", "Equal"], ["!=", "NotEqual"],
|
|
21
|
+
["<", "LessThan"], ["<=", "LessThanOrEqual"], [">", "GreaterThan"], [">=", "GreaterThanOrEqual"],
|
|
22
|
+
["&&", "And"], ["||", "Or"]
|
|
23
|
+
])
|
|
24
|
+
/** @type {Map<string, readonly [string, Set<string>]>} */
|
|
25
|
+
const binaryNodeOperators = new Map([
|
|
26
|
+
["additive_expression", ["additive_operator", new Set(["+", "-"])]],
|
|
27
|
+
["multiplicative_expression", ["multiplicative_operator", new Set(["*"])]],
|
|
28
|
+
["equality_expression", ["equality_operator", new Set(["==", "!="])]],
|
|
29
|
+
["relational_expression", ["relational_operator", new Set(["<", "<=", ">", ">="])]],
|
|
30
|
+
["logical_and_expression", ["logical_and_operator", new Set(["&&"])]],
|
|
31
|
+
["logical_or_expression", ["logical_or_operator", new Set(["||"])]]
|
|
32
|
+
])
|
|
33
|
+
const commentTypes = new Set(["comment", "documentation_comment"])
|
|
34
|
+
/** @type {Map<string, import("../semantic/operators.js").AdaptedOperation>} */
|
|
35
|
+
const helperOperations = new Map([
|
|
36
|
+
["_semantifoldIntegerAdd", "Add"], ["_semantifoldIntegerSubtract", "Subtract"],
|
|
37
|
+
["_semantifoldIntegerMultiply", "Multiply"], ["_semantifoldIntegerNegate", "Negate"]
|
|
38
|
+
])
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Verifies a binding-reported UTF-16 index through the shared UTF-8 converter.
|
|
42
|
+
* @param {string} source Complete source.
|
|
43
|
+
* @param {number} index Binding index.
|
|
44
|
+
* @returns {number} Verified UTF-16 offset.
|
|
45
|
+
*/
|
|
46
|
+
function bindingIndexToUtf16Offset(source, index) {
|
|
47
|
+
if (!Number.isInteger(index) || index < 0 || index > source.length ||
|
|
48
|
+
index > 0 && index < source.length && /[\uD800-\uDBFF]/u.test(source[index - 1]) && /[\uDC00-\uDFFF]/u.test(source[index])) {
|
|
49
|
+
throw new RangeError("Invalid Tree-sitter source index: " + index)
|
|
50
|
+
}
|
|
51
|
+
return utf8ByteOffsetToUtf16Offset(source, new TextEncoder().encode(source.slice(0, index)).length)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Finds the first unpaired UTF-16 surrogate.
|
|
56
|
+
* @param {string} source Complete source.
|
|
57
|
+
* @returns {number} Invalid offset or -1.
|
|
58
|
+
*/
|
|
59
|
+
function firstLoneSurrogateOffset(source) {
|
|
60
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
61
|
+
const unit = source.charCodeAt(index)
|
|
62
|
+
|
|
63
|
+
if (unit >= 0xD800 && unit <= 0xDBFF) {
|
|
64
|
+
const next = source.charCodeAt(index + 1)
|
|
65
|
+
|
|
66
|
+
if (next >= 0xDC00 && next <= 0xDFFF) index += 1
|
|
67
|
+
else return index
|
|
68
|
+
} else if (unit >= 0xDC00 && unit <= 0xDFFF) return index
|
|
69
|
+
}
|
|
70
|
+
return -1
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Consumes exact qualified Dart CST shapes without retaining parser values. */
|
|
74
|
+
class DartReader {
|
|
75
|
+
/**
|
|
76
|
+
* Creates a reader bound to one exact Dart source unit.
|
|
77
|
+
* @param {string} filename Original filename.
|
|
78
|
+
* @param {string} source Complete source.
|
|
79
|
+
*/
|
|
80
|
+
constructor(filename, source) {
|
|
81
|
+
this.filename = filename
|
|
82
|
+
this.source = source
|
|
83
|
+
/** @type {Set<string>} */
|
|
84
|
+
this.functionNames = new Set()
|
|
85
|
+
this.runtime = false
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Builds a semantic source location from one parser node.
|
|
90
|
+
* @param {import("tree-sitter").SyntaxNode} node Parser node.
|
|
91
|
+
* @returns {import("../semantic/types.js").SourceLocation} Exact location.
|
|
92
|
+
*/
|
|
93
|
+
location(node) {
|
|
94
|
+
return locationFromOffsets(this.filename, this.source, bindingIndexToUtf16Offset(this.source, node.startIndex),
|
|
95
|
+
bindingIndexToUtf16Offset(this.source, node.endIndex))
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Rejects one parser node with a located unsupported-syntax diagnostic.
|
|
100
|
+
* @param {import("tree-sitter").SyntaxNode} node Violating node.
|
|
101
|
+
* @param {string} [detail] Profile detail.
|
|
102
|
+
* @returns {never} Always throws.
|
|
103
|
+
*/
|
|
104
|
+
fail(node, detail = node.type) {
|
|
105
|
+
unsupportedSyntax("dart", detail, this.location(node))
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Traverses every named, anonymous, extra, field, and recovery edge.
|
|
110
|
+
* @param {import("tree-sitter").SyntaxNode} root Complete parser root.
|
|
111
|
+
* @returns {void}
|
|
112
|
+
*/
|
|
113
|
+
validateTree(root) {
|
|
114
|
+
const pending = [{depth: 0, node: root, visited: false}]
|
|
115
|
+
|
|
116
|
+
while (pending.length > 0) {
|
|
117
|
+
const current = pending.pop()
|
|
118
|
+
|
|
119
|
+
if (!current) throw new Error("Dart traversal lost a pending parser node.")
|
|
120
|
+
const {depth, node, visited} = current
|
|
121
|
+
|
|
122
|
+
if (depth > 512) this.fail(node, "CST exceeds the 512-level traversal limit")
|
|
123
|
+
if (!visited) {
|
|
124
|
+
pending.push({depth, node, visited: true})
|
|
125
|
+
for (let index = node.childCount - 1; index >= 0; index -= 1) {
|
|
126
|
+
const child = node.child(index)
|
|
127
|
+
|
|
128
|
+
if (!child) throw new Error(`Tree-sitter omitted child ${index} of ${node.type}.`)
|
|
129
|
+
node.fieldNameForChild(index)
|
|
130
|
+
pending.push({depth: depth + 1, node: child, visited: false})
|
|
131
|
+
}
|
|
132
|
+
continue
|
|
133
|
+
}
|
|
134
|
+
if (node.hasError || node.isError || node.isMissing) {
|
|
135
|
+
throw new SemantifoldDiagnostic({
|
|
136
|
+
code: "PARSE_ERROR",
|
|
137
|
+
language: "dart",
|
|
138
|
+
location: this.location(node),
|
|
139
|
+
message: `Tree-sitter exposed recovery at '${node.type}'.`
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
if (["annotation", "import_or_export", "library_name", "part_header", "part_of_header"].includes(node.type)) {
|
|
143
|
+
this.fail(node, node.type)
|
|
144
|
+
}
|
|
145
|
+
if (commentTypes.has(node.type) && (/(?:@dart\s*=|dartfmt|ignore(?:_for_file)?\s*:|language[-_ ]version)/iu.test(node.text) ||
|
|
146
|
+
/^\/\/ dart format (?:off|on)[\t ]*$/u.test(node.text))) {
|
|
147
|
+
this.fail(node, "directive-bearing comment")
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Omits only ordinary comments already covered by exhaustive traversal.
|
|
154
|
+
* @param {import("tree-sitter").SyntaxNode} node Parent node.
|
|
155
|
+
* @returns {import("tree-sitter").SyntaxNode[]} Structural children.
|
|
156
|
+
*/
|
|
157
|
+
parts(node) {
|
|
158
|
+
return node.children.filter((child) => !commentTypes.has(child.type))
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Verifies an exact structural child sequence.
|
|
163
|
+
* @param {import("tree-sitter").SyntaxNode} node Shape owner.
|
|
164
|
+
* @param {(import("tree-sitter").SyntaxNode | string)[]} expected Exact child sequence.
|
|
165
|
+
* @returns {void}
|
|
166
|
+
*/
|
|
167
|
+
shape(node, expected) {
|
|
168
|
+
const actual = this.parts(node)
|
|
169
|
+
|
|
170
|
+
if (actual.length != expected.length) this.fail(actual[Math.min(actual.length, expected.length)] ?? node, `${node.type} child shape`)
|
|
171
|
+
for (let index = 0; index < expected.length; index += 1) {
|
|
172
|
+
const wanted = expected[index]
|
|
173
|
+
const child = actual[index]
|
|
174
|
+
const matches = typeof wanted == "string" ? child.text == wanted : child.id == wanted.id
|
|
175
|
+
|
|
176
|
+
if (!matches) this.fail(child, `${node.type} child shape`)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Consumes an Orchard operator wrapper whether its token is aliased into the wrapper or exposed anonymously.
|
|
182
|
+
* @param {import("tree-sitter").SyntaxNode} node Operator node.
|
|
183
|
+
* @returns {void}
|
|
184
|
+
*/
|
|
185
|
+
operatorShape(node) {
|
|
186
|
+
const parts = this.parts(node)
|
|
187
|
+
|
|
188
|
+
if (parts.length > 1 || parts.length == 1 && (parts[0].isNamed || parts[0].text != node.text)) {
|
|
189
|
+
this.fail(node, `${node.type} child shape`)
|
|
190
|
+
}
|
|
191
|
+
this.shape(node, parts.length == 0 ? [] : [node.text])
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Returns exactly one parser child carrying a requested field name.
|
|
196
|
+
* @param {import("tree-sitter").SyntaxNode} node Field owner.
|
|
197
|
+
* @param {string} name Field name.
|
|
198
|
+
* @returns {import("tree-sitter").SyntaxNode} Sole field node.
|
|
199
|
+
*/
|
|
200
|
+
field(node, name) {
|
|
201
|
+
const matches = []
|
|
202
|
+
|
|
203
|
+
for (let index = 0; index < node.childCount; index += 1) {
|
|
204
|
+
const child = node.child(index)
|
|
205
|
+
|
|
206
|
+
if (!child) throw new Error(`Tree-sitter omitted child ${index} of ${node.type}.`)
|
|
207
|
+
if (node.fieldNameForChild(index) == name) matches.push(child)
|
|
208
|
+
}
|
|
209
|
+
if (matches.length != 1) this.fail(node, `expected one '${name}' field`)
|
|
210
|
+
return matches[0]
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Validates and returns a source identifier.
|
|
215
|
+
* @param {import("tree-sitter").SyntaxNode} node Identifier token.
|
|
216
|
+
* @param {boolean} [allowMain] Whether fixed main is expected.
|
|
217
|
+
* @param {boolean} [allowPrint] Whether fixed print is expected.
|
|
218
|
+
* @returns {string} Accepted name.
|
|
219
|
+
*/
|
|
220
|
+
identifier(node, allowMain = false, allowPrint = false) {
|
|
221
|
+
const name = node.text
|
|
222
|
+
|
|
223
|
+
if (node.type != "identifier" || !(allowMain && name == "main") && !(allowPrint && name == "print") &&
|
|
224
|
+
!isDartIdentifier(name)) this.fail(node, "reserved or unsupported identifier")
|
|
225
|
+
return name
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Converts one explicitly named scalar type.
|
|
230
|
+
* @param {import("tree-sitter").SyntaxNode} node Type node.
|
|
231
|
+
* @returns {import("../semantic/types.js").TypeReference} Scalar type.
|
|
232
|
+
*/
|
|
233
|
+
scalarType(node) {
|
|
234
|
+
if (node.type != "type_identifier" || this.parts(node).length != 0) this.fail(node, "scalar type shape")
|
|
235
|
+
const type = sourceScalarType("dart", node.text, this.location(node))
|
|
236
|
+
|
|
237
|
+
if (!type) this.fail(node, `unsupported scalar type '${node.text}'`)
|
|
238
|
+
return type
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Converts one explicit scalar or void result type.
|
|
243
|
+
* @param {import("tree-sitter").SyntaxNode} node Return type node.
|
|
244
|
+
* @returns {import("../semantic/types.js").SemanticFunctionReturnType} Function return type.
|
|
245
|
+
*/
|
|
246
|
+
returnType(node) {
|
|
247
|
+
if (node.type == "void_type") {
|
|
248
|
+
const parts = this.parts(node)
|
|
249
|
+
|
|
250
|
+
if (node.text != "void" || parts.length > 1 || parts.length == 1 && parts[0].text != "void") {
|
|
251
|
+
this.fail(node, "void type shape")
|
|
252
|
+
}
|
|
253
|
+
this.shape(node, parts)
|
|
254
|
+
const type = {kind: /** @type {const} */ ("TypeReference"), name: /** @type {const} */ ("void")}
|
|
255
|
+
|
|
256
|
+
return withParserRanges(type, {type: this.location(node)})
|
|
257
|
+
}
|
|
258
|
+
return this.scalarType(node)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Decodes one ordinary noninterpolated, non-raw, single-line Dart string.
|
|
263
|
+
* @param {import("tree-sitter").SyntaxNode} node String node.
|
|
264
|
+
* @returns {string} Decoded Unicode scalar value.
|
|
265
|
+
*/
|
|
266
|
+
string(node) {
|
|
267
|
+
const parts = this.parts(node)
|
|
268
|
+
const delimiter = node.text[0]
|
|
269
|
+
|
|
270
|
+
if (node.type != "string_literal" || !["\"", "'"].includes(delimiter) || node.text.startsWith(delimiter.repeat(3)) ||
|
|
271
|
+
node.text.at(-1) != delimiter || parts.length < 2 || parts[0].text != delimiter || parts.at(-1)?.text != delimiter ||
|
|
272
|
+
parts.slice(1, -1).some(({type}) => type != "escape_sequence")) {
|
|
273
|
+
this.fail(node, "ordinary noninterpolated string literal required")
|
|
274
|
+
}
|
|
275
|
+
this.shape(node, parts)
|
|
276
|
+
const source = node.text.slice(1, -1)
|
|
277
|
+
const simpleEscapes = new Map([
|
|
278
|
+
["b", "\b"], ["f", "\f"], ["n", "\n"], ["r", "\r"], ["t", "\t"], ["v", "\v"],
|
|
279
|
+
["$", "$"], ["'", "'"], ['"', '"'], ["\\", "\\"]
|
|
280
|
+
])
|
|
281
|
+
let value = ""
|
|
282
|
+
|
|
283
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
284
|
+
const character = source[index]
|
|
285
|
+
|
|
286
|
+
if (character != "\\") {
|
|
287
|
+
if (character == "\n" || character == "\r" || character == delimiter || character == "$") {
|
|
288
|
+
this.fail(node, "invalid or interpolated string content")
|
|
289
|
+
}
|
|
290
|
+
value += character
|
|
291
|
+
continue
|
|
292
|
+
}
|
|
293
|
+
const escaped = source[++index]
|
|
294
|
+
|
|
295
|
+
if (simpleEscapes.has(escaped)) value += simpleEscapes.get(escaped)
|
|
296
|
+
else if (escaped == "x") {
|
|
297
|
+
const hexadecimal = source.slice(index + 1, index + 3)
|
|
298
|
+
|
|
299
|
+
if (!/^[0-9A-Fa-f]{2}$/u.test(hexadecimal)) this.fail(node, "unsupported string escape")
|
|
300
|
+
value += String.fromCodePoint(Number.parseInt(hexadecimal, 16))
|
|
301
|
+
index += 2
|
|
302
|
+
} else if (escaped == "u") {
|
|
303
|
+
if (source[index + 1] == "{") {
|
|
304
|
+
const closing = source.indexOf("}", index + 2)
|
|
305
|
+
const hexadecimal = closing < 0 ? "" : source.slice(index + 2, closing)
|
|
306
|
+
const codePoint = /^[0-9A-Fa-f]{1,6}$/u.test(hexadecimal) ? Number.parseInt(hexadecimal, 16) : -1
|
|
307
|
+
|
|
308
|
+
if (codePoint < 0 || codePoint > 0x10FFFF) this.fail(node, "unsupported string escape")
|
|
309
|
+
value += String.fromCodePoint(codePoint)
|
|
310
|
+
index = closing
|
|
311
|
+
} else {
|
|
312
|
+
const hexadecimal = source.slice(index + 1, index + 5)
|
|
313
|
+
|
|
314
|
+
if (!/^[0-9A-Fa-f]{4}$/u.test(hexadecimal)) this.fail(node, "unsupported string escape")
|
|
315
|
+
value += String.fromCharCode(Number.parseInt(hexadecimal, 16))
|
|
316
|
+
index += 4
|
|
317
|
+
}
|
|
318
|
+
} else this.fail(node, "unsupported string escape")
|
|
319
|
+
}
|
|
320
|
+
if (!hasOnlyUnicodeScalars(value)) this.fail(node, "invalid Unicode string literal")
|
|
321
|
+
return value
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Extracts exact required positional arguments.
|
|
326
|
+
* @param {import("tree-sitter").SyntaxNode} part Call argument part.
|
|
327
|
+
* @returns {import("tree-sitter").SyntaxNode[][]} Argument expression edges.
|
|
328
|
+
*/
|
|
329
|
+
arguments(part) {
|
|
330
|
+
const partChildren = this.parts(part)
|
|
331
|
+
|
|
332
|
+
if (part.type != "argument_part" || partChildren.length != 1 || partChildren[0].type != "arguments") {
|
|
333
|
+
this.fail(part, "call argument part shape")
|
|
334
|
+
}
|
|
335
|
+
this.shape(part, [partChildren[0]])
|
|
336
|
+
const list = partChildren[0]
|
|
337
|
+
const listParts = this.parts(list)
|
|
338
|
+
|
|
339
|
+
if (listParts.length < 2 || listParts[0].text != "(" || listParts.at(-1)?.text != ")") {
|
|
340
|
+
this.fail(list, "call argument list shape")
|
|
341
|
+
}
|
|
342
|
+
const interior = listParts.slice(1, -1)
|
|
343
|
+
const hasGeneratedTrailingComma = this.runtime && interior.at(-1)?.text == ","
|
|
344
|
+
const argumentParts = hasGeneratedTrailingComma ? interior.slice(0, -1) : interior
|
|
345
|
+
const arguments_ = argumentParts.filter((_child, index) => index % 2 == 0)
|
|
346
|
+
|
|
347
|
+
this.shape(list, ["(", ...arguments_.flatMap((argument, index) => index ? [",", argument] : [argument]),
|
|
348
|
+
...(hasGeneratedTrailingComma ? [","] : []), ")"])
|
|
349
|
+
return arguments_.map((argument) => {
|
|
350
|
+
if (argument.type != "argument") this.fail(argument, "required positional argument")
|
|
351
|
+
const expressionParts = this.parts(argument)
|
|
352
|
+
|
|
353
|
+
if (expressionParts.length == 0) this.fail(argument, "empty argument")
|
|
354
|
+
return expressionParts
|
|
355
|
+
})
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Converts one direct method-invocation node whose function is a plain identifier.
|
|
360
|
+
* @param {import("tree-sitter").SyntaxNode[]} nodes Exact one-node call.
|
|
361
|
+
* @param {import("tree-sitter").SyntaxNode} owner Expression owner.
|
|
362
|
+
* @param {boolean} [allowPrint] Whether this is the print scaffold.
|
|
363
|
+
* @returns {import("../semantic/types.js").Expression} Semantic call or private generated operation.
|
|
364
|
+
*/
|
|
365
|
+
call(nodes, owner, allowPrint = false) {
|
|
366
|
+
if (nodes.length != 1 || nodes[0].type != "method_invocation") {
|
|
367
|
+
this.fail(nodes[0] ?? owner, "direct call shape")
|
|
368
|
+
}
|
|
369
|
+
const invocation = nodes[0]
|
|
370
|
+
const parts = this.parts(invocation)
|
|
371
|
+
const functionNode = this.field(invocation, "function")
|
|
372
|
+
const argumentPart = this.field(invocation, "arguments")
|
|
373
|
+
|
|
374
|
+
if (parts.length != 2 || parts[0].id != functionNode.id || functionNode.type != "identifier" ||
|
|
375
|
+
parts[1].id != argumentPart.id || argumentPart.type != "argument_part") this.fail(invocation, "direct call shape")
|
|
376
|
+
this.shape(invocation, [functionNode, argumentPart])
|
|
377
|
+
const name = functionNode.text
|
|
378
|
+
const helper = this.runtime ? helperOperations.get(name) : undefined
|
|
379
|
+
const location = this.location(invocation)
|
|
380
|
+
const argumentNodes = this.arguments(argumentPart)
|
|
381
|
+
|
|
382
|
+
if (this.runtime && name == "_semantifoldBoolean") {
|
|
383
|
+
if (argumentNodes.length != 1) this.fail(argumentPart, "Dart Boolean literal helper shape")
|
|
384
|
+
const literal = this.expression(argumentNodes[0], owner)
|
|
385
|
+
|
|
386
|
+
if (literal.kind != "BooleanLiteral") this.fail(argumentPart, "Dart Boolean literal helper shape")
|
|
387
|
+
return literal
|
|
388
|
+
}
|
|
389
|
+
if (helper) {
|
|
390
|
+
const count = helper == "Negate" ? 1 : 2
|
|
391
|
+
|
|
392
|
+
if (argumentNodes.length != count) this.fail(argumentPart, "Dart checked-integer helper shape")
|
|
393
|
+
if (helper == "Negate") {
|
|
394
|
+
return /** @type {import("../semantic/types.js").Expression} */ (/** @type {unknown} */ (
|
|
395
|
+
withAdaptedOperation(withParserRanges({
|
|
396
|
+
kind: /** @type {const} */ ("UnaryExpression"), location,
|
|
397
|
+
operand: this.expression(argumentNodes[0], owner)
|
|
398
|
+
}, {operator: this.location(functionNode)}), helper)))
|
|
399
|
+
}
|
|
400
|
+
return /** @type {import("../semantic/types.js").Expression} */ (/** @type {unknown} */ (
|
|
401
|
+
withAdaptedOperation(withParserRanges({
|
|
402
|
+
kind: /** @type {const} */ ("BinaryExpression"), left: this.expression(argumentNodes[0], owner), location,
|
|
403
|
+
right: this.expression(argumentNodes[1], owner)
|
|
404
|
+
}, {operator: this.location(functionNode)}), helper)))
|
|
405
|
+
}
|
|
406
|
+
this.identifier(functionNode, false, allowPrint)
|
|
407
|
+
|
|
408
|
+
return withParserRanges({
|
|
409
|
+
arguments: argumentNodes.map((argument) => this.expression(argument, owner)),
|
|
410
|
+
callee: name,
|
|
411
|
+
kind: /** @type {const} */ ("CallExpression"),
|
|
412
|
+
location
|
|
413
|
+
}, {callee: this.location(functionNode)})
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Recursively compares one source support subtree with the canonical runtime CST.
|
|
418
|
+
* @param {import("tree-sitter").SyntaxNode} actual Source node.
|
|
419
|
+
* @param {import("tree-sitter").SyntaxNode} expected Canonical node.
|
|
420
|
+
* @returns {void}
|
|
421
|
+
*/
|
|
422
|
+
compareTree(actual, expected) {
|
|
423
|
+
if (actual.type != expected.type || actual.isNamed != expected.isNamed || actual.isExtra != expected.isExtra ||
|
|
424
|
+
actual.childCount != expected.childCount) this.fail(actual, "modified Dart support node")
|
|
425
|
+
if (actual.childCount == 0 && actual.text != expected.text) this.fail(actual, "modified Dart support token")
|
|
426
|
+
for (let index = 0; index < actual.childCount; index += 1) {
|
|
427
|
+
const left = actual.child(index)
|
|
428
|
+
const right = expected.child(index)
|
|
429
|
+
|
|
430
|
+
if (!left || !right || actual.fieldNameForChild(index) != expected.fieldNameForChild(index)) {
|
|
431
|
+
this.fail(left ?? actual, "modified Dart support field")
|
|
432
|
+
}
|
|
433
|
+
this.compareTree(left, right)
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Converts an expression represented by one or more adjacent Dart CST nodes.
|
|
439
|
+
* @param {import("tree-sitter").SyntaxNode[]} nodes Expression nodes.
|
|
440
|
+
* @param {import("tree-sitter").SyntaxNode} owner Owning node.
|
|
441
|
+
* @returns {import("../semantic/types.js").Expression} Semantic expression.
|
|
442
|
+
*/
|
|
443
|
+
expression(nodes, owner) {
|
|
444
|
+
if (nodes.length == 1 && nodes[0].type == "method_invocation") return this.call(nodes, owner)
|
|
445
|
+
if (nodes.length != 1) return this.fail(nodes[0] ?? owner, "scalar expression shape")
|
|
446
|
+
const node = nodes[0]
|
|
447
|
+
const location = this.location(node)
|
|
448
|
+
|
|
449
|
+
if (node.type == "identifier") {
|
|
450
|
+
const name = this.identifier(node)
|
|
451
|
+
|
|
452
|
+
if (this.functionNames.has(name)) this.fail(node, "function tear-off")
|
|
453
|
+
return withParserRanges({kind: /** @type {const} */ ("IdentifierExpression"), location, name}, {name: location})
|
|
454
|
+
}
|
|
455
|
+
if (node.type == "decimal_integer_literal") {
|
|
456
|
+
if (this.parts(node).length != 0 || !/^(?:0|[1-9][0-9]*)$/u.test(node.text)) this.fail(node, "noncanonical integer literal")
|
|
457
|
+
const value = Number(BigInt(node.text))
|
|
458
|
+
|
|
459
|
+
if (!Number.isSafeInteger(value)) this.fail(node, "non-safe integer literal")
|
|
460
|
+
return withParserRanges({kind: /** @type {const} */ ("IntegerLiteral"), location, value}, {literal: location})
|
|
461
|
+
}
|
|
462
|
+
if (node.type == "true" || node.type == "false") {
|
|
463
|
+
this.shape(node, [node.type])
|
|
464
|
+
return withParserRanges({kind: /** @type {const} */ ("BooleanLiteral"), location, value: node.type == "true"}, {literal: location})
|
|
465
|
+
}
|
|
466
|
+
if (node.type == "string_literal") {
|
|
467
|
+
return withParserRanges({kind: /** @type {const} */ ("StringLiteral"), location, value: this.string(node)}, {literal: location})
|
|
468
|
+
}
|
|
469
|
+
if (node.type == "parenthesized_expression") {
|
|
470
|
+
const parts = this.parts(node)
|
|
471
|
+
|
|
472
|
+
if (parts.length < 3 || parts[0].text != "(" || parts.at(-1)?.text != ")") this.fail(node, "parenthesized expression shape")
|
|
473
|
+
this.shape(node, ["(", ...parts.slice(1, -1), ")"])
|
|
474
|
+
return this.expression(parts.slice(1, -1), node)
|
|
475
|
+
}
|
|
476
|
+
if (node.type == "unary_expression") {
|
|
477
|
+
const parts = this.parts(node)
|
|
478
|
+
const operator = parts[0]
|
|
479
|
+
|
|
480
|
+
if (parts.length < 2 || operator.type != "prefix_operator" || !["-", "!"].includes(operator.text)) {
|
|
481
|
+
this.fail(node, "unary operator")
|
|
482
|
+
}
|
|
483
|
+
const operatorParts = this.parts(operator)
|
|
484
|
+
|
|
485
|
+
if (operatorParts.length != 1 || operatorParts[0].text != operator.text ||
|
|
486
|
+
!["minus_operator", "negation_operator"].includes(operatorParts[0].type)) this.fail(operator, "unary operator shape")
|
|
487
|
+
this.operatorShape(operatorParts[0])
|
|
488
|
+
this.shape(node, [operator, ...parts.slice(1)])
|
|
489
|
+
return /** @type {import("../semantic/types.js").Expression} */ (/** @type {unknown} */ (withAdaptedOperation(withParserRanges({
|
|
490
|
+
kind: /** @type {const} */ ("UnaryExpression"),
|
|
491
|
+
location,
|
|
492
|
+
operand: this.expression(parts.slice(1), node)
|
|
493
|
+
}, {operator: this.location(operator)}), operator.text == "-" ? "Negate" : "Not")))
|
|
494
|
+
}
|
|
495
|
+
if (binaryNodeOperators.has(node.type)) return this.binary(node)
|
|
496
|
+
return this.fail(node)
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Converts a bounded binary expression tree.
|
|
501
|
+
* @param {import("tree-sitter").SyntaxNode} node Binary expression node.
|
|
502
|
+
* @returns {import("../semantic/types.js").Expression} Semantic binary expression.
|
|
503
|
+
*/
|
|
504
|
+
binary(node) {
|
|
505
|
+
/** @type {import("tree-sitter").SyntaxNode[][]} */
|
|
506
|
+
const operands = []
|
|
507
|
+
/** @type {import("tree-sitter").SyntaxNode[]} */
|
|
508
|
+
const operators = []
|
|
509
|
+
let current = node
|
|
510
|
+
|
|
511
|
+
while (true) {
|
|
512
|
+
const parts = this.parts(current)
|
|
513
|
+
const declaration = binaryNodeOperators.get(current.type)
|
|
514
|
+
const operatorType = declaration?.[0]
|
|
515
|
+
const accepted = declaration?.[1]
|
|
516
|
+
const operatorIndexes = parts.flatMap((part, index) => part.type == operatorType ? [index] : [])
|
|
517
|
+
|
|
518
|
+
if (operatorIndexes.length != 1) this.fail(current, `${current.type} child shape`)
|
|
519
|
+
const operatorIndex = operatorIndexes[0]
|
|
520
|
+
const operator = parts[operatorIndex]
|
|
521
|
+
|
|
522
|
+
if (operatorIndex == 0 || operatorIndex == parts.length - 1 || !accepted?.has(operator.text) ||
|
|
523
|
+
!binaryOperations.has(operator.text)) this.fail(operator, "unsupported binary operator")
|
|
524
|
+
this.operatorShape(operator)
|
|
525
|
+
this.shape(current, parts)
|
|
526
|
+
operands.push(parts.slice(0, operatorIndex))
|
|
527
|
+
operators.push(operator)
|
|
528
|
+
const right = parts.slice(operatorIndex + 1)
|
|
529
|
+
|
|
530
|
+
if (right.length == 1 && right[0].type == node.type) current = right[0]
|
|
531
|
+
else {
|
|
532
|
+
operands.push(right)
|
|
533
|
+
break
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
let expression = this.expression(operands[0], node)
|
|
537
|
+
|
|
538
|
+
for (let index = 0; index < operators.length; index += 1) {
|
|
539
|
+
const operator = operators[index]
|
|
540
|
+
const operation = binaryOperations.get(operator.text)
|
|
541
|
+
const right = this.expression(operands[index + 1], node)
|
|
542
|
+
const first = operands[0][0]
|
|
543
|
+
const last = operands[index + 1].at(-1)
|
|
544
|
+
const location = first && last
|
|
545
|
+
? locationFromOffsets(this.filename, this.source, bindingIndexToUtf16Offset(this.source, first.startIndex),
|
|
546
|
+
bindingIndexToUtf16Offset(this.source, last.endIndex))
|
|
547
|
+
: this.location(node)
|
|
548
|
+
|
|
549
|
+
expression = /** @type {import("../semantic/types.js").Expression} */ (withAdaptedOperation(withParserRanges({
|
|
550
|
+
kind: /** @type {const} */ ("BinaryExpression"), left: expression, location, right
|
|
551
|
+
}, {operator: this.location(operator)}), /** @type {import("../semantic/operators.js").AdaptedOperation} */ (operation)))
|
|
552
|
+
}
|
|
553
|
+
return expression
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Converts one explicitly typed initialized local declaration.
|
|
558
|
+
* @param {import("tree-sitter").SyntaxNode} node Local declaration.
|
|
559
|
+
* @returns {import("../semantic/types.js").LocalDeclaration} Semantic local.
|
|
560
|
+
*/
|
|
561
|
+
local(node) {
|
|
562
|
+
const declarationParts = this.parts(node)
|
|
563
|
+
|
|
564
|
+
if (node.type != "local_variable_declaration" || declarationParts.length != 2 ||
|
|
565
|
+
declarationParts[0].type != "initialized_variable_definition" || declarationParts[1].text != ";") {
|
|
566
|
+
this.fail(node, "local declaration shape")
|
|
567
|
+
}
|
|
568
|
+
this.shape(node, [declarationParts[0], ";"])
|
|
569
|
+
const definition = declarationParts[0]
|
|
570
|
+
const parts = this.parts(definition)
|
|
571
|
+
|
|
572
|
+
if (parts[0]?.type == "inferred_type" || parts[0]?.type == "final_builtin" && parts[1]?.type == "identifier") {
|
|
573
|
+
return missingType("dart", "Local", this.location(definition))
|
|
574
|
+
}
|
|
575
|
+
const immutable = parts[0]?.type == "final_builtin"
|
|
576
|
+
const typeIndex = immutable ? 1 : 0
|
|
577
|
+
const typeNode = parts[typeIndex]
|
|
578
|
+
const nameNode = parts[typeIndex + 1]
|
|
579
|
+
const equals = parts[typeIndex + 2]
|
|
580
|
+
const expressionParts = parts.slice(typeIndex + 3)
|
|
581
|
+
|
|
582
|
+
if (parts.length < typeIndex + 4 || typeNode?.type != "type_identifier" || nameNode?.type != "identifier" ||
|
|
583
|
+
equals?.text != "=" || expressionParts.length == 0 || immutable && parts[0].text != "final") {
|
|
584
|
+
this.fail(parts[0] ?? definition, "explicitly typed initialized local required")
|
|
585
|
+
}
|
|
586
|
+
this.shape(definition, [...(immutable ? [parts[0]] : []), typeNode, nameNode, "=", ...expressionParts])
|
|
587
|
+
if (this.field(definition, "name").id != nameNode.id) this.fail(nameNode, "local name field")
|
|
588
|
+
const name = this.identifier(nameNode)
|
|
589
|
+
|
|
590
|
+
return withParserRanges({
|
|
591
|
+
initializer: this.expression(expressionParts, definition),
|
|
592
|
+
kind: /** @type {const} */ ("LocalDeclaration"),
|
|
593
|
+
location: this.location(node),
|
|
594
|
+
mutable: !immutable,
|
|
595
|
+
name,
|
|
596
|
+
type: this.scalarType(typeNode)
|
|
597
|
+
}, {name: this.location(nameNode), operator: this.location(equals)})
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Converts one simple identifier assignment.
|
|
602
|
+
* @param {import("tree-sitter").SyntaxNode} node Assignment expression.
|
|
603
|
+
* @returns {import("../semantic/types.js").AssignmentStatement} Semantic assignment.
|
|
604
|
+
*/
|
|
605
|
+
assignment(node) {
|
|
606
|
+
const parts = this.parts(node)
|
|
607
|
+
const target = parts[0]
|
|
608
|
+
const operator = parts[1]
|
|
609
|
+
|
|
610
|
+
if (node.type != "assignment_expression" || parts.length < 3 || target?.type != "assignable_expression" ||
|
|
611
|
+
operator?.type != "assignment_operator" || operator.text != "=") this.fail(node, "simple assignment shape")
|
|
612
|
+
const targetParts = this.parts(target)
|
|
613
|
+
|
|
614
|
+
if (targetParts.length != 1 || targetParts[0].type != "identifier") this.fail(target, "simple assignment target")
|
|
615
|
+
this.shape(target, [targetParts[0]])
|
|
616
|
+
this.operatorShape(operator)
|
|
617
|
+
this.shape(node, [target, "=", ...parts.slice(2)])
|
|
618
|
+
const name = this.identifier(targetParts[0])
|
|
619
|
+
const targetLocation = this.location(targetParts[0])
|
|
620
|
+
|
|
621
|
+
return withParserRanges({
|
|
622
|
+
expression: this.expression(parts.slice(2), node),
|
|
623
|
+
kind: /** @type {const} */ ("AssignmentStatement"),
|
|
624
|
+
location: this.location(node),
|
|
625
|
+
target: withParserRanges({kind: /** @type {const} */ ("IdentifierExpression"), location: targetLocation, name}, {name: targetLocation})
|
|
626
|
+
}, {operator: this.location(operator)})
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Converts one supported Dart statement.
|
|
631
|
+
* @param {import("tree-sitter").SyntaxNode} node Source statement.
|
|
632
|
+
* @returns {import("../semantic/types.js").Statement} Semantic statement.
|
|
633
|
+
*/
|
|
634
|
+
statement(node) {
|
|
635
|
+
if (node.type == "local_variable_declaration") return this.local(node)
|
|
636
|
+
if (node.type == "if_statement") return this.conditional(node)
|
|
637
|
+
if (node.type == "return_statement") {
|
|
638
|
+
const parts = this.parts(node)
|
|
639
|
+
|
|
640
|
+
if (parts.length < 2 || parts[0].text != "return" || parts.at(-1)?.text != ";") this.fail(node, "return statement shape")
|
|
641
|
+
this.shape(node, ["return", ...parts.slice(1, -1), ";"])
|
|
642
|
+
const expressionParts = parts.slice(1, -1)
|
|
643
|
+
|
|
644
|
+
return {
|
|
645
|
+
...(expressionParts.length > 0 ? {expression: this.expression(expressionParts, node)} : {}),
|
|
646
|
+
kind: /** @type {const} */ ("ReturnStatement"),
|
|
647
|
+
location: this.location(node)
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
if (node.type == "expression_statement") {
|
|
651
|
+
const parts = this.parts(node)
|
|
652
|
+
|
|
653
|
+
if (parts.length < 2 || parts.at(-1)?.text != ";") this.fail(node, "expression statement shape")
|
|
654
|
+
this.shape(node, [...parts.slice(0, -1), ";"])
|
|
655
|
+
if (parts.length == 2 && parts[0].type == "assignment_expression") return this.assignment(parts[0])
|
|
656
|
+
const callParts = parts.slice(0, -1)
|
|
657
|
+
const callChildren = callParts.length == 1 && callParts[0].type == "method_invocation"
|
|
658
|
+
? this.parts(callParts[0]) : []
|
|
659
|
+
const print = callChildren[0]?.type == "identifier" && callChildren[0].text == "print"
|
|
660
|
+
const expression = this.call(callParts, node, print)
|
|
661
|
+
|
|
662
|
+
if (print) {
|
|
663
|
+
if (expression.kind != "CallExpression" || expression.arguments.length != 1) {
|
|
664
|
+
this.fail(callParts[0], "print argument count")
|
|
665
|
+
}
|
|
666
|
+
return {expression: expression.arguments[0], kind: /** @type {const} */ ("PrintStatement"), location: this.location(node)}
|
|
667
|
+
}
|
|
668
|
+
if (expression.kind != "CallExpression") this.fail(callParts[0], "generated helper expression statement")
|
|
669
|
+
return {expression, kind: /** @type {const} */ ("ExpressionStatement"), location: this.location(node)}
|
|
670
|
+
}
|
|
671
|
+
return this.fail(node)
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Consumes one compiler-owned read that prevents Dart from rejecting an otherwise unread local.
|
|
676
|
+
* @param {import("tree-sitter").SyntaxNode} node Candidate source statement.
|
|
677
|
+
* @param {import("../semantic/types.js").Statement | undefined} previous Previous semantic statement.
|
|
678
|
+
* @returns {boolean} Whether the exact authenticated support statement was consumed.
|
|
679
|
+
*/
|
|
680
|
+
generatedLocalUse(node, previous) {
|
|
681
|
+
if (!this.runtime || previous?.kind != "LocalDeclaration" || node.type != "expression_statement") return false
|
|
682
|
+
const statementParts = this.parts(node)
|
|
683
|
+
const invocation = statementParts[0]
|
|
684
|
+
|
|
685
|
+
if (statementParts.length != 2 || invocation?.type != "method_invocation" || statementParts[1].text != ";") return false
|
|
686
|
+
const invocationParts = this.parts(invocation)
|
|
687
|
+
const functionNode = this.field(invocation, "function")
|
|
688
|
+
const argumentPart = this.field(invocation, "arguments")
|
|
689
|
+
|
|
690
|
+
if (invocationParts.length != 2 || invocationParts[0].id != functionNode.id || functionNode.type != "identifier" ||
|
|
691
|
+
functionNode.text != "_semantifoldUse" || invocationParts[1].id != argumentPart.id || argumentPart.type != "argument_part") {
|
|
692
|
+
return false
|
|
693
|
+
}
|
|
694
|
+
this.shape(node, [invocation, ";"])
|
|
695
|
+
this.shape(invocation, [functionNode, argumentPart])
|
|
696
|
+
const arguments_ = this.arguments(argumentPart)
|
|
697
|
+
|
|
698
|
+
if (arguments_.length != 1 || arguments_[0].length != 1 || arguments_[0][0].type != "identifier" ||
|
|
699
|
+
this.identifier(arguments_[0][0]) != previous.name) this.fail(argumentPart, "Dart analyzer local-use support shape")
|
|
700
|
+
return true
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Converts a braced Dart block.
|
|
705
|
+
* @param {import("tree-sitter").SyntaxNode} node Braced block.
|
|
706
|
+
* @returns {import("../semantic/types.js").Block} Semantic block.
|
|
707
|
+
*/
|
|
708
|
+
block(node) {
|
|
709
|
+
const parts = this.parts(node)
|
|
710
|
+
|
|
711
|
+
if (node.type != "block" || parts.length < 2 || parts[0].text != "{" || parts.at(-1)?.text != "}") {
|
|
712
|
+
this.fail(node, "braced block shape")
|
|
713
|
+
}
|
|
714
|
+
this.shape(node, ["{", ...parts.slice(1, -1), "}"])
|
|
715
|
+
/** @type {import("../semantic/types.js").Statement[]} */
|
|
716
|
+
const statements = []
|
|
717
|
+
|
|
718
|
+
for (const statement of parts.slice(1, -1)) {
|
|
719
|
+
if (!this.generatedLocalUse(statement, statements.at(-1))) statements.push(this.statement(statement))
|
|
720
|
+
}
|
|
721
|
+
return {
|
|
722
|
+
kind: /** @type {const} */ ("Block"),
|
|
723
|
+
location: this.location(node),
|
|
724
|
+
statements
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/**
|
|
729
|
+
* Unwraps a synchronous braced function body.
|
|
730
|
+
* @param {import("tree-sitter").SyntaxNode} body Function body wrapper.
|
|
731
|
+
* @returns {import("../semantic/types.js").Block} Semantic body.
|
|
732
|
+
*/
|
|
733
|
+
functionBody(body) {
|
|
734
|
+
const parts = this.parts(body)
|
|
735
|
+
|
|
736
|
+
if (body.type != "function_body" || parts.length != 1 || parts[0].type != "block") {
|
|
737
|
+
this.fail(body, "synchronous braced function body required")
|
|
738
|
+
}
|
|
739
|
+
this.shape(body, [parts[0]])
|
|
740
|
+
return this.block(parts[0])
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* Converts one strict-Boolean conditional.
|
|
745
|
+
* @param {import("tree-sitter").SyntaxNode} node Strict Boolean conditional.
|
|
746
|
+
* @returns {import("../semantic/types.js").IfStatement} Semantic conditional.
|
|
747
|
+
*/
|
|
748
|
+
conditional(node) {
|
|
749
|
+
const parts = this.parts(node)
|
|
750
|
+
const consequence = this.field(node, "consequence")
|
|
751
|
+
const consequenceIndex = parts.findIndex(({id}) => id == consequence.id)
|
|
752
|
+
|
|
753
|
+
if (node.type != "if_statement" || parts[0]?.text != "if" || parts[1]?.text != "(" || consequence.type != "block" ||
|
|
754
|
+
consequenceIndex < 4 || parts[consequenceIndex - 1]?.text != ")") this.fail(node, "if statement shape")
|
|
755
|
+
const conditionParts = parts.slice(2, consequenceIndex - 1)
|
|
756
|
+
|
|
757
|
+
if (conditionParts.length == 0) this.fail(node, "empty if condition")
|
|
758
|
+
const statement = /** @type {import("../semantic/types.js").IfStatement} */ ({
|
|
759
|
+
condition: this.expression(conditionParts, node),
|
|
760
|
+
consequent: this.block(consequence),
|
|
761
|
+
kind: /** @type {const} */ ("IfStatement"),
|
|
762
|
+
location: this.location(node)
|
|
763
|
+
})
|
|
764
|
+
if (parts.length == consequenceIndex + 1) {
|
|
765
|
+
this.shape(node, ["if", "(", ...conditionParts, ")", consequence])
|
|
766
|
+
return statement
|
|
767
|
+
}
|
|
768
|
+
const alternative = this.field(node, "alternative")
|
|
769
|
+
|
|
770
|
+
if (parts.length != consequenceIndex + 3 || parts[consequenceIndex + 1]?.text != "else" ||
|
|
771
|
+
parts[consequenceIndex + 2]?.id != alternative.id) this.fail(alternative, "if alternate shape")
|
|
772
|
+
this.shape(node, ["if", "(", ...conditionParts, ")", consequence, "else", alternative])
|
|
773
|
+
if (alternative.type == "block") return {...statement, alternate: this.block(alternative)}
|
|
774
|
+
if (alternative.type == "if_statement") {
|
|
775
|
+
return {...statement, alternate: {
|
|
776
|
+
kind: /** @type {const} */ ("Block"), location: this.location(alternative), statements: [this.conditional(alternative)]
|
|
777
|
+
}}
|
|
778
|
+
}
|
|
779
|
+
return this.fail(alternative, "braced block or else-if required")
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* Converts one required explicitly typed positional parameter.
|
|
784
|
+
* @param {import("tree-sitter").SyntaxNode} node Required positional parameter.
|
|
785
|
+
* @returns {import("../semantic/types.js").Parameter} Semantic parameter.
|
|
786
|
+
*/
|
|
787
|
+
parameter(node) {
|
|
788
|
+
const parts = this.parts(node)
|
|
789
|
+
|
|
790
|
+
if (parts.length == 1 && parts[0].type == "identifier") return missingType("dart", "Parameter", this.location(node))
|
|
791
|
+
if (node.type != "formal_parameter" || parts.length != 2 || parts[0].type != "type_identifier" ||
|
|
792
|
+
parts[1].type != "identifier") this.fail(node, "required explicitly typed positional parameter")
|
|
793
|
+
this.shape(node, [parts[0], parts[1]])
|
|
794
|
+
if (this.field(node, "name").id != parts[1].id) this.fail(parts[1], "parameter name field")
|
|
795
|
+
const name = this.identifier(parts[1])
|
|
796
|
+
|
|
797
|
+
return withParserRanges({
|
|
798
|
+
kind: /** @type {const} */ ("Parameter"), location: this.location(node), name, type: this.scalarType(parts[0])
|
|
799
|
+
}, {name: this.location(parts[1])})
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/**
|
|
803
|
+
* Converts a required positional parameter list.
|
|
804
|
+
* @param {import("tree-sitter").SyntaxNode} node Parameter list.
|
|
805
|
+
* @returns {import("../semantic/types.js").Parameter[]} Semantic parameters.
|
|
806
|
+
*/
|
|
807
|
+
parameters(node) {
|
|
808
|
+
const parts = this.parts(node)
|
|
809
|
+
|
|
810
|
+
if (node.type != "formal_parameter_list" || parts.length < 2 || parts[0].text != "(" || parts.at(-1)?.text != ")") {
|
|
811
|
+
this.fail(node, "formal parameter list shape")
|
|
812
|
+
}
|
|
813
|
+
const interior = parts.slice(1, -1)
|
|
814
|
+
const hasGeneratedTrailingComma = this.runtime && interior.at(-1)?.text == ","
|
|
815
|
+
const parameterParts = hasGeneratedTrailingComma ? interior.slice(0, -1) : interior
|
|
816
|
+
const parameters = parameterParts.filter((_child, index) => index % 2 == 0)
|
|
817
|
+
|
|
818
|
+
this.shape(node, ["(", ...parameters.flatMap((parameter, index) => index ? [",", parameter] : [parameter]),
|
|
819
|
+
...(hasGeneratedTrailingComma ? [","] : []), ")"])
|
|
820
|
+
return parameters.map((parameter) => this.parameter(parameter))
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
/**
|
|
824
|
+
* Converts one ordinary top-level function declaration.
|
|
825
|
+
* @param {import("tree-sitter").SyntaxNode} signature Function signature.
|
|
826
|
+
* @param {import("tree-sitter").SyntaxNode} body Function body.
|
|
827
|
+
* @returns {import("../semantic/types.js").FunctionDeclaration} Semantic function.
|
|
828
|
+
*/
|
|
829
|
+
function(signature, body) {
|
|
830
|
+
const parts = this.parts(signature)
|
|
831
|
+
|
|
832
|
+
if (signature.type != "function_signature") this.fail(signature, "function signature")
|
|
833
|
+
if (parts.length == 2 && parts[0].type == "identifier") return missingType("dart", "Function return", this.location(signature))
|
|
834
|
+
if (parts.length != 3 || !["type_identifier", "void_type"].includes(parts[0].type) || parts[1].type != "identifier" ||
|
|
835
|
+
parts[2].type != "formal_parameter_list") this.fail(signature, "explicit top-level function signature")
|
|
836
|
+
this.shape(signature, [parts[0], parts[1], parts[2]])
|
|
837
|
+
if (this.field(signature, "name").id != parts[1].id) this.fail(parts[1], "function name field")
|
|
838
|
+
const name = this.identifier(parts[1])
|
|
839
|
+
const location = locationFromOffsets(this.filename, this.source, bindingIndexToUtf16Offset(this.source, signature.startIndex),
|
|
840
|
+
bindingIndexToUtf16Offset(this.source, body.endIndex))
|
|
841
|
+
|
|
842
|
+
return withParserRanges({
|
|
843
|
+
body: this.functionBody(body),
|
|
844
|
+
kind: /** @type {const} */ ("FunctionDeclaration"),
|
|
845
|
+
location,
|
|
846
|
+
name,
|
|
847
|
+
parameters: this.parameters(parts[2]),
|
|
848
|
+
returnType: this.returnType(parts[0])
|
|
849
|
+
}, {name: this.location(parts[1])})
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
/**
|
|
853
|
+
* Converts the exact void main entry shell.
|
|
854
|
+
* @param {import("tree-sitter").SyntaxNode} signature Main signature.
|
|
855
|
+
* @param {import("tree-sitter").SyntaxNode} body Main body.
|
|
856
|
+
* @returns {import("../semantic/types.js").EntryPoint} Semantic entry point.
|
|
857
|
+
*/
|
|
858
|
+
main(signature, body) {
|
|
859
|
+
const parts = this.parts(signature)
|
|
860
|
+
|
|
861
|
+
if (signature.type != "function_signature" || parts.length != 3 || parts[0].type != "void_type" ||
|
|
862
|
+
this.identifier(parts[1], true) != "main" || parts[2].type != "formal_parameter_list") {
|
|
863
|
+
this.fail(signature, "exact void main() signature required")
|
|
864
|
+
}
|
|
865
|
+
this.shape(signature, [parts[0], parts[1], parts[2]])
|
|
866
|
+
this.shape(parts[2], ["(", ")"])
|
|
867
|
+
const location = locationFromOffsets(this.filename, this.source, bindingIndexToUtf16Offset(this.source, signature.startIndex),
|
|
868
|
+
bindingIndexToUtf16Offset(this.source, body.endIndex))
|
|
869
|
+
|
|
870
|
+
return {body: this.functionBody(body), kind: /** @type {const} */ ("EntryPoint"), location}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
/**
|
|
874
|
+
* Converts one complete Dart compilation unit.
|
|
875
|
+
* @param {import("tree-sitter").SyntaxNode} root Complete Dart program.
|
|
876
|
+
* @returns {import("../semantic/types.js").SemanticModule} Semantic module.
|
|
877
|
+
*/
|
|
878
|
+
module(root) {
|
|
879
|
+
this.validateTree(root)
|
|
880
|
+
if (root.type != "program") this.fail(root, "program root")
|
|
881
|
+
const parts = this.parts(root)
|
|
882
|
+
const supportTree = parser.parse(dartRuntime)
|
|
883
|
+
const support = this.parts(supportTree.rootNode)
|
|
884
|
+
let supportIndex = 0
|
|
885
|
+
|
|
886
|
+
while (supportIndex < parts.length && supportIndex < support.length &&
|
|
887
|
+
parts[supportIndex].type == support[supportIndex].type) {
|
|
888
|
+
this.compareTree(parts[supportIndex], support[supportIndex])
|
|
889
|
+
supportIndex += 1
|
|
890
|
+
}
|
|
891
|
+
if (supportIndex > 0 && supportIndex != support.length) {
|
|
892
|
+
this.fail(parts[supportIndex - 1], "complete Dart support required")
|
|
893
|
+
}
|
|
894
|
+
this.runtime = supportIndex == support.length
|
|
895
|
+
const semanticParts = parts.slice(supportIndex)
|
|
896
|
+
|
|
897
|
+
if (semanticParts.length == 0) this.fail(root, "program without main")
|
|
898
|
+
/** @type {{body: import("tree-sitter").SyntaxNode, signature: import("tree-sitter").SyntaxNode}[]} */
|
|
899
|
+
const declarations = []
|
|
900
|
+
|
|
901
|
+
for (const declaration of semanticParts) {
|
|
902
|
+
if (declaration.type != "function_declaration") this.fail(declaration, "top-level function declaration required")
|
|
903
|
+
const signature = this.field(declaration, "signature")
|
|
904
|
+
const body = this.field(declaration, "body")
|
|
905
|
+
const declarationParts = this.parts(declaration)
|
|
906
|
+
|
|
907
|
+
if (declarationParts.length != 2 || declarationParts[0].id != signature.id || signature.type != "function_signature" ||
|
|
908
|
+
declarationParts[1].id != body.id || body.type != "function_body") {
|
|
909
|
+
this.fail(declaration, "paired top-level function signature and body required")
|
|
910
|
+
}
|
|
911
|
+
this.shape(declaration, [signature, body])
|
|
912
|
+
declarations.push({body, signature})
|
|
913
|
+
}
|
|
914
|
+
const mainIndexes = declarations.flatMap(({signature}, index) => this.parts(signature)[1]?.text == "main" ? [index] : [])
|
|
915
|
+
|
|
916
|
+
if (mainIndexes.length != 1) this.fail(root, "exactly one main declaration required")
|
|
917
|
+
const mainIndex = mainIndexes[0]
|
|
918
|
+
|
|
919
|
+
this.functionNames = new Set(declarations.map(({signature}) => this.parts(signature)[1]?.text).filter(Boolean))
|
|
920
|
+
|
|
921
|
+
if (mainIndex != declarations.length - 1) this.fail(declarations[mainIndex + 1].signature, "declaration after main")
|
|
922
|
+
const main = declarations[mainIndex]
|
|
923
|
+
const module = {
|
|
924
|
+
entryPoint: this.main(main.signature, main.body),
|
|
925
|
+
functions: declarations.slice(0, mainIndex).map(({body, signature}) => this.function(signature, body)),
|
|
926
|
+
kind: /** @type {const} */ ("Module"),
|
|
927
|
+
location: this.location(root)
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
validateParsedModule(module, "dart")
|
|
931
|
+
return module
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/**
|
|
936
|
+
* Parses the strict Task 029 Dart VM/native profile through the qualified grammar.
|
|
937
|
+
* @param {{filename: string, source: string}} input Exact source request.
|
|
938
|
+
* @returns {import("../semantic/types.js").SemanticModule} Semantic module.
|
|
939
|
+
*/
|
|
940
|
+
export function parseDart({filename, source}) {
|
|
941
|
+
if (!hasOnlyUnicodeScalars(source)) {
|
|
942
|
+
const offset = firstLoneSurrogateOffset(source)
|
|
943
|
+
|
|
944
|
+
throw new SemantifoldDiagnostic({
|
|
945
|
+
code: "PARSE_ERROR", language: "dart", location: locationFromOffsets(filename, source, offset, offset + 1),
|
|
946
|
+
message: "Dart source contains an invalid lone UTF-16 surrogate."
|
|
947
|
+
})
|
|
948
|
+
}
|
|
949
|
+
let tree
|
|
950
|
+
|
|
951
|
+
try {
|
|
952
|
+
tree = parser.parse(source)
|
|
953
|
+
} catch (error) {
|
|
954
|
+
throw new SemantifoldDiagnostic({
|
|
955
|
+
cause: error instanceof Error ? error : undefined,
|
|
956
|
+
code: "PARSE_ERROR",
|
|
957
|
+
language: "dart",
|
|
958
|
+
location: moduleLocation(filename, source),
|
|
959
|
+
message: "The qualified Dart parser could not produce a complete CST."
|
|
960
|
+
})
|
|
961
|
+
}
|
|
962
|
+
return new DartReader(filename, source).module(tree.rootNode)
|
|
963
|
+
}
|