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
package/src/frontends/java.js
CHANGED
|
@@ -6,7 +6,10 @@ import {locationFromOffsets, moduleLocation} from "../semantic/location.js"
|
|
|
6
6
|
import {withAdaptedOperation} from "../semantic/operators.js"
|
|
7
7
|
import {withParserRanges} from "../semantic/provenance.js"
|
|
8
8
|
import {hasOnlyUnicodeScalars} from "../semantic/scalars.js"
|
|
9
|
-
import {
|
|
9
|
+
import {isTask034ResourceProbe} from "../semantic/capabilities.js"
|
|
10
|
+
import {hasExactEffectSupport} from "../backends/effects.js"
|
|
11
|
+
import {requireSourceReturnType, sourceScalarType} from "./scalars.js"
|
|
12
|
+
import {capabilityFunctionSignatures, instantiatedRecordFieldType, iterationBindingType, iterationOperandType, knownCallReturnType, listType, mapType, optionalType, orderedMapType, ownedResourceType, recordType, referenceType, sameValueType, typeVariable} from "./types.js"
|
|
10
13
|
|
|
11
14
|
/** @type {Readonly<Record<string, string>>} */
|
|
12
15
|
const simpleStringEscapes = Object.freeze({
|
|
@@ -33,6 +36,32 @@ const javaBinaryOperations = new Map([
|
|
|
33
36
|
[">", "GreaterThan"],
|
|
34
37
|
[">=", "GreaterThanOrEqual"]
|
|
35
38
|
])
|
|
39
|
+
const referenceMethodHooks = new Set(["clone", "equals", "finalize", "getClass", "hashCode", "notify", "notifyAll", "toString", "wait"])
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @typedef JavaConversionContext
|
|
43
|
+
* @property {Map<string, import("../semantic/types.js").SemanticBindingType>} bindings - Explicitly typed visible bindings.
|
|
44
|
+
* @property {Map<string, import("../semantic/types.js").ClassDeclaration>} classes - Reference classes by identity.
|
|
45
|
+
* @property {import("../semantic/types.js").ClassDeclaration} [currentClass] - Enclosing reference class.
|
|
46
|
+
* @property {Map<string, import("../semantic/types.js").ErrorDeclaration>} errorNames - Errors by source name.
|
|
47
|
+
* @property {Map<string, import("../semantic/types.js").ErrorDeclaration>} errors - Errors by identity.
|
|
48
|
+
* @property {Set<string>} erasedBindingNames - Source bindings consumed by exact semantic folds in this scope.
|
|
49
|
+
* @property {Map<string, JavaFunctionSignature>} functions - Explicit module function signatures.
|
|
50
|
+
* @property {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} recordNames - Nominal declarations by source name.
|
|
51
|
+
* @property {Map<string, import("../semantic/types.js").RecordDeclaration>} records - Records by identity.
|
|
52
|
+
* @property {import("../semantic/types.js").SemanticFunctionReturnType | undefined} returnType - Enclosing return type.
|
|
53
|
+
* @property {Map<string, import("../semantic/types.js").TypeParameter>} [typeParameters] - Declaration-scoped parameters.
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @typedef JavaFunctionSignature
|
|
58
|
+
* @property {import("../semantic/types.js").SourceLocation} location - Complete declaration location.
|
|
59
|
+
* @property {import("../semantic/types.js").SourceLocation} nameLocation - Parser-owned name location.
|
|
60
|
+
* @property {string} name - Function name.
|
|
61
|
+
* @property {import("../semantic/types.js").Parameter[]} parameters - Semantic parameters.
|
|
62
|
+
* @property {import("../semantic/types.js").SemanticFunctionReturnType} returnType - Semantic return type.
|
|
63
|
+
* @property {import("../semantic/types.js").TypeParameter[]} [typeParameters] - Declaration-scoped parameters.
|
|
64
|
+
*/
|
|
36
65
|
|
|
37
66
|
/**
|
|
38
67
|
* Returns all direct child syntax nodes.
|
|
@@ -110,14 +139,46 @@ function nodeText(node, source) {
|
|
|
110
139
|
return source.slice(node.from, node.to)
|
|
111
140
|
}
|
|
112
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Proves an exact parser-structured Java qualified identifier.
|
|
144
|
+
* @param {import("@lezer/common").SyntaxNode} node - Identifier or field-access node.
|
|
145
|
+
* @param {string} source - Complete source.
|
|
146
|
+
* @returns {string[] | undefined} Exact structural name parts.
|
|
147
|
+
*/
|
|
148
|
+
function qualifiedNameParts(node, source) {
|
|
149
|
+
if (["Identifier", "TypeName"].includes(node.name)) return [nodeText(node, source)]
|
|
150
|
+
if (!["FieldAccess", "ScopedTypeName"].includes(node.name)) return undefined
|
|
151
|
+
const children = structuralChildren(node)
|
|
152
|
+
|
|
153
|
+
if (children.length != 2) return undefined
|
|
154
|
+
const left = qualifiedNameParts(children[0], source)
|
|
155
|
+
const right = qualifiedNameParts(children[1], source)
|
|
156
|
+
|
|
157
|
+
return left && right ? [...left, ...right] : undefined
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Checks the only accepted Java Optional type/factory symbol spellings.
|
|
162
|
+
* @param {import("@lezer/common").SyntaxNode} node - Parser-qualified symbol node.
|
|
163
|
+
* @param {string} source - Complete source.
|
|
164
|
+
* @returns {boolean} Whether the node identifies java.util.Optional or Optional.
|
|
165
|
+
*/
|
|
166
|
+
function isJavaOptionalSymbol(node, source) {
|
|
167
|
+
const parts = qualifiedNameParts(node, source)
|
|
168
|
+
|
|
169
|
+
return parts?.join(".") == "java.util.Optional" || parts?.join(".") == "Optional"
|
|
170
|
+
}
|
|
171
|
+
|
|
113
172
|
/**
|
|
114
173
|
* Converts a supported Java expression node.
|
|
115
174
|
* @param {import("@lezer/common").SyntaxNode} node - Lezer expression.
|
|
116
175
|
* @param {string} filename - Source filename.
|
|
117
176
|
* @param {string} source - Complete source.
|
|
177
|
+
* @param {JavaConversionContext} context - Typed lexical conversion context.
|
|
178
|
+
* @param {import("../semantic/types.js").SemanticValueType} [expectedType] - Contextual semantic type.
|
|
118
179
|
* @returns {import("../semantic/types.js").Expression} Semantic expression.
|
|
119
180
|
*/
|
|
120
|
-
function convertExpression(node, filename, source) {
|
|
181
|
+
function convertExpression(node, filename, source, context, expectedType) {
|
|
121
182
|
const location = nodeLocation(node, filename, source)
|
|
122
183
|
|
|
123
184
|
if (node.name == "ParenthesizedExpression") {
|
|
@@ -127,13 +188,38 @@ function convertExpression(node, filename, source) {
|
|
|
127
188
|
return unsupportedSyntax("java", "unsupported parenthesized expression", location)
|
|
128
189
|
}
|
|
129
190
|
|
|
130
|
-
return convertExpression(children[0], filename, source)
|
|
191
|
+
return convertExpression(children[0], filename, source, context, expectedType)
|
|
131
192
|
}
|
|
132
193
|
|
|
133
194
|
if (node.name == "Identifier") {
|
|
134
195
|
return withParserRanges({kind: /** @type {const} */ ("IdentifierExpression"), location, name: nodeText(node, source)}, {name: location})
|
|
135
196
|
}
|
|
136
197
|
|
|
198
|
+
if (node.name == "this") {
|
|
199
|
+
if (!context.currentClass) return unsupportedSyntax("java", "receiver outside a reference class", location)
|
|
200
|
+
return withParserRanges({classId: /** @type {string} */ (context.currentClass.id),
|
|
201
|
+
kind: /** @type {const} */ ("ReceiverExpression"), location}, {receiver: location})
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (node.name == "FieldAccess") {
|
|
205
|
+
const children = structuralChildren(node)
|
|
206
|
+
const receiver = children[0]
|
|
207
|
+
const member = children[1]
|
|
208
|
+
|
|
209
|
+
if (children.length != 2 || receiver?.name != "this" || member?.name != "Identifier" || !context.currentClass) {
|
|
210
|
+
if (receiver && knownExpressionType(receiver, filename, source, context)?.kind == "ReferenceType") {
|
|
211
|
+
return unsupportedSyntax("java", "private reference-class field access", location)
|
|
212
|
+
}
|
|
213
|
+
return unsupportedSyntax("java", "field access outside the declaring receiver", location)
|
|
214
|
+
}
|
|
215
|
+
const field = context.currentClass.fields.find((candidate) => candidate.name == nodeText(member, source))
|
|
216
|
+
|
|
217
|
+
if (!field) return unsupportedSyntax("java", `unknown private field '${nodeText(member, source)}'`, nodeLocation(member, filename, source))
|
|
218
|
+
return withParserRanges({field: /** @type {string} */ (field.id), kind: /** @type {const} */ ("PrivateFieldRead"), location,
|
|
219
|
+
receiver: /** @type {import("../semantic/types.js").ReceiverExpression} */ (
|
|
220
|
+
convertExpression(receiver, filename, source, context))}, {member: nodeLocation(member, filename, source)})
|
|
221
|
+
}
|
|
222
|
+
|
|
137
223
|
if (node.name == "IntegerLiteral") {
|
|
138
224
|
const value = Number(nodeText(node, source))
|
|
139
225
|
|
|
@@ -150,6 +236,50 @@ function convertExpression(node, filename, source) {
|
|
|
150
236
|
return withParserRanges({kind: /** @type {const} */ ("StringLiteral"), location, value: decodeStringLiteral(node, filename, source)}, {literal: location})
|
|
151
237
|
}
|
|
152
238
|
|
|
239
|
+
if (node.name == "null") return unsupportedSyntax("java", "null reference", location)
|
|
240
|
+
|
|
241
|
+
if (node.name == "ObjectCreationExpression") {
|
|
242
|
+
const children = structuralChildren(node)
|
|
243
|
+
const typeNode = children.find((child) => ["GenericType", "TypeName", "ScopedTypeName"].includes(child.name))
|
|
244
|
+
const nameNode = typeNode?.name == "GenericType" ? structuralChildren(typeNode)[0] : typeNode
|
|
245
|
+
const argumentList = node.getChild("ArgumentList")
|
|
246
|
+
const declaration = nameNode ? context.recordNames.get(nodeText(nameNode, source)) : undefined
|
|
247
|
+
|
|
248
|
+
if (!typeNode || !nameNode || !argumentList || !declaration || node.getChild("ClassBody")) {
|
|
249
|
+
return unsupportedSyntax("java", "construction of an unknown nominal or anonymous class", location)
|
|
250
|
+
}
|
|
251
|
+
const argumentNodes = structuralChildren(argumentList)
|
|
252
|
+
if (declaration.kind == "ClassDeclaration") {
|
|
253
|
+
if (typeNode.name == "GenericType") return unsupportedSyntax("java", "generic reference construction", nodeLocation(typeNode, filename, source))
|
|
254
|
+
const classLocation = nodeLocation(nameNode, filename, source)
|
|
255
|
+
|
|
256
|
+
return withParserRanges({arguments: argumentNodes.map((argument, index) =>
|
|
257
|
+
convertExpression(argument, filename, source, context, declaration.constructor.parameters[index]?.type)),
|
|
258
|
+
kind: /** @type {const} */ ("ReferenceConstruction"), location,
|
|
259
|
+
reference: referenceType(/** @type {string} */ (declaration.id), classLocation)}, {class: classLocation})
|
|
260
|
+
}
|
|
261
|
+
if (declaration.kind == "EffectResourceDeclaration") {
|
|
262
|
+
return unsupportedSyntax("java", "owned resource construction", nodeLocation(typeNode, filename, source))
|
|
263
|
+
}
|
|
264
|
+
const typeArgumentsNode = typeNode.getChild("TypeArguments")
|
|
265
|
+
|
|
266
|
+
if (typeNode.name == "GenericType" && !typeArgumentsNode) {
|
|
267
|
+
return unsupportedSyntax("java", "malformed generic record construction", nodeLocation(typeNode, filename, source))
|
|
268
|
+
}
|
|
269
|
+
const typeArguments = typeArgumentsNode
|
|
270
|
+
? structuralChildren(typeArgumentsNode).map((argument) => convertJavaTypeArgument(
|
|
271
|
+
argument, `Record '${declaration.name}' application`, location, filename, source, context.recordNames, context.typeParameters))
|
|
272
|
+
: undefined
|
|
273
|
+
return withParserRanges({
|
|
274
|
+
arguments: argumentNodes.map((argument, index) =>
|
|
275
|
+
convertExpression(argument, filename, source, context,
|
|
276
|
+
instantiatedRecordFieldType(declaration, typeArguments, index))),
|
|
277
|
+
kind: /** @type {const} */ ("RecordConstruction"),
|
|
278
|
+
location,
|
|
279
|
+
record: recordType(/** @type {string} */ (declaration.id), nodeLocation(nameNode, filename, source), typeArguments)
|
|
280
|
+
}, {record: nodeLocation(nameNode, filename, source)})
|
|
281
|
+
}
|
|
282
|
+
|
|
153
283
|
if (node.name == "UnaryExpression") {
|
|
154
284
|
const children = structuralChildren(node)
|
|
155
285
|
const operatorNode = node.getChild("LogicOp") ?? node.getChild("ArithOp")
|
|
@@ -160,14 +290,15 @@ function convertExpression(node, filename, source) {
|
|
|
160
290
|
return unsupportedSyntax("java", "unary expression", location)
|
|
161
291
|
}
|
|
162
292
|
|
|
163
|
-
if (operator == "!" && operand.name == "MethodInvocation" && isEqualsInvocation(operand, source)
|
|
164
|
-
|
|
293
|
+
if (operator == "!" && operand.name == "MethodInvocation" && isEqualsInvocation(operand, source) &&
|
|
294
|
+
!isZeroArgumentRecordMemberInvocation(operand, filename, source, context)) {
|
|
295
|
+
return convertStringEquality(operand, true, location, nodeLocation(operatorNode, filename, source), filename, source, context)
|
|
165
296
|
}
|
|
166
297
|
|
|
167
298
|
const semantic = withAdaptedOperation(withParserRanges({
|
|
168
299
|
kind: "UnaryExpression",
|
|
169
300
|
location,
|
|
170
|
-
operand: convertExpression(operand, filename, source)
|
|
301
|
+
operand: convertExpression(operand, filename, source, context)
|
|
171
302
|
}, {operator: nodeLocation(operatorNode, filename, source)}), operator == "!" ? "Not" : "Negate")
|
|
172
303
|
|
|
173
304
|
return /** @type {import("../semantic/types.js").Expression} */ (/** @type {unknown} */ (semantic))
|
|
@@ -190,33 +321,222 @@ function convertExpression(node, filename, source) {
|
|
|
190
321
|
|
|
191
322
|
const semantic = withAdaptedOperation(withParserRanges({
|
|
192
323
|
kind: "BinaryExpression",
|
|
193
|
-
left: convertExpression(operands[0], filename, source),
|
|
324
|
+
left: convertExpression(operands[0], filename, source, context),
|
|
194
325
|
location,
|
|
195
|
-
right: convertExpression(operands[1], filename, source)
|
|
326
|
+
right: convertExpression(operands[1], filename, source, context)
|
|
196
327
|
}, {operator: nodeLocation(operatorNode, filename, source)}), /** @type {import("../semantic/operators.js").AdaptedOperation} */ (javaBinaryOperations.get(operator)))
|
|
197
328
|
|
|
198
329
|
return /** @type {import("../semantic/types.js").Expression} */ (/** @type {unknown} */ (semantic))
|
|
199
330
|
}
|
|
200
331
|
|
|
201
332
|
if (node.name == "MethodInvocation") {
|
|
202
|
-
if (isEqualsInvocation(node, source)) {
|
|
333
|
+
if (isEqualsInvocation(node, source) && !isZeroArgumentRecordMemberInvocation(node, filename, source, context)) {
|
|
203
334
|
const methodName = requiredChild(node, "MethodName", filename, source)
|
|
204
335
|
|
|
205
|
-
return convertStringEquality(node, false, location, nodeLocation(methodName, filename, source), filename, source)
|
|
336
|
+
return convertStringEquality(node, false, location, nodeLocation(methodName, filename, source), filename, source, context)
|
|
206
337
|
}
|
|
207
338
|
|
|
208
|
-
const unsupportedReceiver = structuralChildren(node).find((child) => child.name != "MethodName" && child.name != "ArgumentList")
|
|
209
|
-
|
|
210
|
-
if (unsupportedReceiver) return unsupportedSyntax("java", "method invocation receiver", nodeLocation(unsupportedReceiver, filename, source))
|
|
211
|
-
|
|
212
339
|
const methodName = requiredChild(node, "MethodName", filename, source)
|
|
213
340
|
const argumentList = requiredChild(node, "ArgumentList", filename, source)
|
|
214
341
|
const argumentNodes = structuralChildren(argumentList)
|
|
342
|
+
const receiver = structuralChildren(node).find((child) => child.name != "MethodName" && child.name != "ArgumentList")
|
|
343
|
+
const method = nodeText(methodName, source)
|
|
344
|
+
const receiverText = receiver ? nodeText(receiver, source) : ""
|
|
345
|
+
|
|
346
|
+
if (receiver?.name == "Identifier" && method == "getMessage" && argumentNodes.length == 0 &&
|
|
347
|
+
context.bindings.get(receiverText)?.kind == "ErrorType") {
|
|
348
|
+
const receiverLocation = nodeLocation(receiver, filename, source)
|
|
349
|
+
|
|
350
|
+
return withParserRanges({
|
|
351
|
+
kind: /** @type {const} */ ("ErrorMessageRead"),
|
|
352
|
+
location,
|
|
353
|
+
receiver: withParserRanges({kind: /** @type {const} */ ("IdentifierExpression"), location: receiverLocation, name: receiverText}, {
|
|
354
|
+
name: receiverLocation
|
|
355
|
+
})
|
|
356
|
+
}, {member: nodeLocation(methodName, filename, source)})
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (receiver && ["getClass", "hashCode", "toString", "clone", "notify", "notifyAll", "wait"].includes(method)) {
|
|
360
|
+
return unsupportedSyntax("java", "reflective or Object member access", nodeLocation(methodName, filename, source))
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (receiver && knownExpressionType(receiver, filename, source, context)?.kind == "ReferenceType") {
|
|
364
|
+
const receiverType = /** @type {import("../semantic/types.js").ReferenceType} */ (
|
|
365
|
+
knownExpressionType(receiver, filename, source, context))
|
|
366
|
+
const declaredMethod = context.classes.get(receiverType.declarationId)?.methods.find((candidate) => candidate.name == method)
|
|
367
|
+
|
|
368
|
+
return withParserRanges({arguments: argumentNodes.map((argument, index) =>
|
|
369
|
+
convertExpression(argument, filename, source, context, declaredMethod?.parameters[index]?.type)),
|
|
370
|
+
kind: /** @type {const} */ ("MethodCallExpression"), location, method: declaredMethod?.id ?? method,
|
|
371
|
+
receiver: convertExpression(receiver, filename, source, context)}, {member: nodeLocation(methodName, filename, source)})
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (receiver && isJavaOptionalSymbol(receiver, source)) {
|
|
375
|
+
const factoryLocation = nodeLocation(methodName, filename, source)
|
|
376
|
+
|
|
377
|
+
if (method == "empty" && argumentNodes.length == 0) {
|
|
378
|
+
return withParserRanges({kind: /** @type {const} */ ("OptionalNone"), location}, {
|
|
379
|
+
absence: factoryLocation,
|
|
380
|
+
factory: factoryLocation
|
|
381
|
+
})
|
|
382
|
+
}
|
|
383
|
+
if (method == "of" && argumentNodes.length == 1) {
|
|
384
|
+
return withParserRanges({
|
|
385
|
+
kind: /** @type {const} */ ("OptionalSome"),
|
|
386
|
+
location,
|
|
387
|
+
value: convertExpression(argumentNodes[0], filename, source, context,
|
|
388
|
+
expectedType?.kind == "OptionalType" ? expectedType.valueType : undefined)
|
|
389
|
+
}, {factory: factoryLocation, some: factoryLocation})
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return unsupportedSyntax("java", `unsupported Optional.${method}`, factoryLocation)
|
|
393
|
+
}
|
|
394
|
+
if (receiver?.name == "Identifier" && context.bindings.get(receiverText)?.kind == "OptionalType") {
|
|
395
|
+
const operand = /** @type {import("../semantic/types.js").IdentifierExpression} */ (
|
|
396
|
+
convertExpression(receiver, filename, source, context)
|
|
397
|
+
)
|
|
398
|
+
const operationLocation = nodeLocation(methodName, filename, source)
|
|
399
|
+
|
|
400
|
+
if (method == "isPresent" && argumentNodes.length == 0) {
|
|
401
|
+
return withParserRanges({kind: /** @type {const} */ ("OptionalIsPresent"), location, operand}, {
|
|
402
|
+
operator: operationLocation
|
|
403
|
+
})
|
|
404
|
+
}
|
|
405
|
+
if (method == "get" && argumentNodes.length == 0) {
|
|
406
|
+
return withParserRanges({kind: /** @type {const} */ ("OptionalUnwrap"), location, operand}, {
|
|
407
|
+
operator: operationLocation,
|
|
408
|
+
unwrap: operationLocation
|
|
409
|
+
})
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return unsupportedSyntax("java", `unsupported Optional.${method}`, operationLocation)
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (receiver && method == "of" && receiverText == "java.util.List") {
|
|
416
|
+
const elementType = expectedType?.kind == "ListType" ? expectedType.elementType : undefined
|
|
417
|
+
|
|
418
|
+
return withParserRanges({
|
|
419
|
+
elements: argumentNodes.map((argument) => convertExpression(argument, filename, source, context, elementType)),
|
|
420
|
+
kind: /** @type {const} */ ("ListLiteral"),
|
|
421
|
+
location
|
|
422
|
+
}, {factory: nodeLocation(methodName, filename, source)})
|
|
423
|
+
}
|
|
424
|
+
if (receiver && method == "of" && receiverText == "java.util.Map") {
|
|
425
|
+
if (argumentNodes.length % 2 != 0) return unsupportedSyntax("java", "Map.of odd argument count", location)
|
|
426
|
+
if (argumentNodes.length > 20) {
|
|
427
|
+
return unsupportedSyntax("java", "java.util.Map.of supports at most ten entries", nodeLocation(methodName, filename, source))
|
|
428
|
+
}
|
|
429
|
+
const entries = []
|
|
430
|
+
const valueType = expectedType?.kind == "MapType" ? expectedType.valueType : undefined
|
|
431
|
+
|
|
432
|
+
for (let index = 0; index < argumentNodes.length; index += 2) {
|
|
433
|
+
const keyNode = argumentNodes[index]
|
|
434
|
+
|
|
435
|
+
if (keyNode.name != "StringLiteral") {
|
|
436
|
+
return unsupportedSyntax("java", "map key other than a string literal", nodeLocation(keyNode, filename, source))
|
|
437
|
+
}
|
|
438
|
+
const separator = directChildren(argumentList).find((child) => child.name == "," &&
|
|
439
|
+
child.from >= keyNode.to && child.to <= argumentNodes[index + 1].from)
|
|
440
|
+
|
|
441
|
+
if (!separator) throw new Error("Lezer omitted the Java Map.of entry separator.")
|
|
442
|
+
entries.push(withParserRanges({
|
|
443
|
+
key: /** @type {import("../semantic/types.js").StringLiteral} */ (convertExpression(keyNode, filename, source, context)),
|
|
444
|
+
kind: /** @type {const} */ ("MapEntry"),
|
|
445
|
+
location: locationFromOffsets(filename, source, keyNode.from, argumentNodes[index + 1].to),
|
|
446
|
+
value: convertExpression(argumentNodes[index + 1], filename, source, context, valueType)
|
|
447
|
+
}, {operator: nodeLocation(separator, filename, source)}))
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return withParserRanges({entries, kind: /** @type {const} */ ("MapLiteral"), location}, {
|
|
451
|
+
factory: nodeLocation(methodName, filename, source)
|
|
452
|
+
})
|
|
453
|
+
}
|
|
454
|
+
if (receiver && method == "get" && argumentNodes.length == 1) {
|
|
455
|
+
const receiverType = receiver.name == "Identifier" ? context.bindings.get(nodeText(receiver, source)) : undefined
|
|
456
|
+
|
|
457
|
+
if (receiverType?.kind == "MapType" || receiverType?.kind == "OrderedMapType" ||
|
|
458
|
+
(!receiverType && argumentNodes[0].name == "StringLiteral")) {
|
|
459
|
+
return withParserRanges({
|
|
460
|
+
collection: convertExpression(receiver, filename, source, context),
|
|
461
|
+
key: convertExpression(argumentNodes[0], filename, source, context),
|
|
462
|
+
kind: /** @type {const} */ ("MapLookupExpression"),
|
|
463
|
+
location,
|
|
464
|
+
totality: /** @type {const} */ ("proven")
|
|
465
|
+
}, {operator: nodeLocation(methodName, filename, source)})
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
return withParserRanges({
|
|
469
|
+
collection: convertExpression(receiver, filename, source, context),
|
|
470
|
+
index: convertExpression(argumentNodes[0], filename, source, context),
|
|
471
|
+
kind: /** @type {const} */ ("ListIndexExpression"),
|
|
472
|
+
location,
|
|
473
|
+
totality: /** @type {const} */ ("fail-on-absence")
|
|
474
|
+
}, {operator: nodeLocation(methodName, filename, source)})
|
|
475
|
+
}
|
|
476
|
+
if (receiver && method == "size" && argumentNodes.length == 0) {
|
|
477
|
+
const receiverType = knownExpressionType(receiver, filename, source, context)
|
|
478
|
+
|
|
479
|
+
if (receiverType?.kind == "RecordType") {
|
|
480
|
+
return withParserRanges({
|
|
481
|
+
field: method,
|
|
482
|
+
kind: /** @type {const} */ ("MemberRead"),
|
|
483
|
+
location,
|
|
484
|
+
receiver: convertExpression(receiver, filename, source, context)
|
|
485
|
+
}, {member: nodeLocation(methodName, filename, source)})
|
|
486
|
+
}
|
|
487
|
+
return withParserRanges({
|
|
488
|
+
collection: convertExpression(receiver, filename, source, context),
|
|
489
|
+
kind: /** @type {const} */ ("CollectionSizeExpression"),
|
|
490
|
+
location
|
|
491
|
+
}, {operator: nodeLocation(methodName, filename, source)})
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (receiver && receiverText == "Main" && context.currentClass && !context.bindings.has(receiverText)) {
|
|
495
|
+
const localSignature = context.functions.get(method)
|
|
496
|
+
|
|
497
|
+
if (localSignature) {
|
|
498
|
+
const arguments_ = argumentNodes.map((child, index) =>
|
|
499
|
+
convertExpression(child, filename, source, context, localSignature.parameters[index]?.type))
|
|
500
|
+
|
|
501
|
+
return withParserRanges({arguments: arguments_, callee: method, kind: /** @type {const} */ ("CallExpression"), location}, {
|
|
502
|
+
callee: nodeLocation(methodName, filename, source)
|
|
503
|
+
})
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (receiver) {
|
|
508
|
+
const qualifiedName = `${receiverText}.${method}`
|
|
509
|
+
const qualifiedSignature = context.functions.get(qualifiedName)
|
|
510
|
+
|
|
511
|
+
if (qualifiedSignature) {
|
|
512
|
+
const arguments_ = argumentNodes.map((child, index) =>
|
|
513
|
+
convertExpression(child, filename, source, context, qualifiedSignature.parameters[index]?.type))
|
|
514
|
+
|
|
515
|
+
return withParserRanges({arguments: arguments_, callee: qualifiedName, kind: /** @type {const} */ ("CallExpression"), location}, {
|
|
516
|
+
callee: nodeLocation(methodName, filename, source)
|
|
517
|
+
})
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
if (receiver && argumentNodes.length == 0) {
|
|
522
|
+
return withParserRanges({
|
|
523
|
+
field: method,
|
|
524
|
+
kind: /** @type {const} */ ("MemberRead"),
|
|
525
|
+
location,
|
|
526
|
+
receiver: convertExpression(receiver, filename, source, context)
|
|
527
|
+
}, {member: nodeLocation(methodName, filename, source)})
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const unsupportedReceiver = receiver
|
|
531
|
+
|
|
532
|
+
if (unsupportedReceiver) return unsupportedSyntax("java", "method invocation receiver", nodeLocation(unsupportedReceiver, filename, source))
|
|
215
533
|
const unsupportedArgument = argumentNodes.find((child) => !isSupportedExpressionNode(child))
|
|
216
534
|
|
|
217
535
|
if (unsupportedArgument) return unsupportedSyntax("java", `method argument ${unsupportedArgument.name}`, nodeLocation(unsupportedArgument, filename, source))
|
|
218
536
|
|
|
219
|
-
const
|
|
537
|
+
const signature = context.functions.get(nodeText(methodName, source))
|
|
538
|
+
const arguments_ = argumentNodes.map((child, index) =>
|
|
539
|
+
convertExpression(child, filename, source, context, signature?.parameters[index]?.type))
|
|
220
540
|
|
|
221
541
|
return withParserRanges({arguments: arguments_, callee: nodeText(methodName, source), kind: /** @type {const} */ ("CallExpression"), location}, {
|
|
222
542
|
callee: nodeLocation(methodName, filename, source)
|
|
@@ -239,6 +559,22 @@ function isEqualsInvocation(node, source) {
|
|
|
239
559
|
return methodName != null && nodeText(methodName, source) == "equals" && receivers.length == 1
|
|
240
560
|
}
|
|
241
561
|
|
|
562
|
+
/**
|
|
563
|
+
* Distinguishes a zero-argument nominal field accessor from Java string equality syntax.
|
|
564
|
+
* @param {import("@lezer/common").SyntaxNode} node - Method invocation node.
|
|
565
|
+
* @param {string} filename - Source filename.
|
|
566
|
+
* @param {string} source - Complete source.
|
|
567
|
+
* @param {JavaConversionContext} context - Typed lexical conversion context.
|
|
568
|
+
* @returns {boolean} Whether this is a known record receiver with no arguments.
|
|
569
|
+
*/
|
|
570
|
+
function isZeroArgumentRecordMemberInvocation(node, filename, source, context) {
|
|
571
|
+
const argumentList = node.getChild("ArgumentList")
|
|
572
|
+
const receiver = structuralChildren(node).find((child) => child.name != "MethodName" && child.name != "ArgumentList")
|
|
573
|
+
|
|
574
|
+
return argumentList != null && structuralChildren(argumentList).length == 0 && receiver != null &&
|
|
575
|
+
knownExpressionType(receiver, filename, source, context)?.kind == "RecordType"
|
|
576
|
+
}
|
|
577
|
+
|
|
242
578
|
/**
|
|
243
579
|
* Converts Java `.equals` or its directly negated form without source-text inference.
|
|
244
580
|
* @param {import("@lezer/common").SyntaxNode} node - Parser method invocation.
|
|
@@ -247,9 +583,10 @@ function isEqualsInvocation(node, source) {
|
|
|
247
583
|
* @param {import("../semantic/types.js").SourceLocation} operatorLocation - Parser-owned operator/method location.
|
|
248
584
|
* @param {string} filename - Source filename.
|
|
249
585
|
* @param {string} source - Complete source.
|
|
586
|
+
* @param {JavaConversionContext} context - Typed lexical conversion context.
|
|
250
587
|
* @returns {import("../semantic/types.js").Expression} Adapted equality expression.
|
|
251
588
|
*/
|
|
252
|
-
function convertStringEquality(node, negated, location, operatorLocation, filename, source) {
|
|
589
|
+
function convertStringEquality(node, negated, location, operatorLocation, filename, source, context) {
|
|
253
590
|
const argumentList = requiredChild(node, "ArgumentList", filename, source)
|
|
254
591
|
const arguments_ = structuralChildren(argumentList)
|
|
255
592
|
const receivers = structuralChildren(node).filter((child) => child.name != "MethodName" && child.name != "ArgumentList")
|
|
@@ -268,9 +605,9 @@ function convertStringEquality(node, negated, location, operatorLocation, filena
|
|
|
268
605
|
|
|
269
606
|
const semantic = withAdaptedOperation(withParserRanges({
|
|
270
607
|
kind: "BinaryExpression",
|
|
271
|
-
left: convertExpression(receivers[0], filename, source),
|
|
608
|
+
left: convertExpression(receivers[0], filename, source, context),
|
|
272
609
|
location,
|
|
273
|
-
right: convertExpression(arguments_[0], filename, source)
|
|
610
|
+
right: convertExpression(arguments_[0], filename, source, context)
|
|
274
611
|
}, ranges), negated ? "StringNotEqual" : "StringEqual")
|
|
275
612
|
|
|
276
613
|
return /** @type {import("../semantic/types.js").Expression} */ (/** @type {unknown} */ (semantic))
|
|
@@ -290,6 +627,10 @@ function isSupportedExpressionNode(node) {
|
|
|
290
627
|
"UnaryExpression",
|
|
291
628
|
"BinaryExpression",
|
|
292
629
|
"MethodInvocation",
|
|
630
|
+
"ObjectCreationExpression",
|
|
631
|
+
"FieldAccess",
|
|
632
|
+
"this",
|
|
633
|
+
"null",
|
|
293
634
|
"ParenthesizedExpression"
|
|
294
635
|
].includes(node.name)
|
|
295
636
|
}
|
|
@@ -397,9 +738,10 @@ function decodeStringLiteral(node, filename, source) {
|
|
|
397
738
|
* @param {import("@lezer/common").SyntaxNode} statement - Java statement.
|
|
398
739
|
* @param {string} filename - Source filename.
|
|
399
740
|
* @param {string} source - Complete source.
|
|
741
|
+
* @param {JavaConversionContext} context - Typed lexical conversion context.
|
|
400
742
|
* @returns {import("../semantic/types.js").ReturnStatement} Semantic return.
|
|
401
743
|
*/
|
|
402
|
-
function convertReturn(statement, filename, source) {
|
|
744
|
+
function convertReturn(statement, filename, source, context) {
|
|
403
745
|
const location = nodeLocation(statement, filename, source)
|
|
404
746
|
|
|
405
747
|
if (statement.name != "ReturnStatement") return unsupportedSyntax("java", statement.name, location)
|
|
@@ -407,17 +749,29 @@ function convertReturn(statement, filename, source) {
|
|
|
407
749
|
const expressionNodes = directChildren(statement).filter((child) => child.name != "return" && child.name != ";")
|
|
408
750
|
const expression = expressionNodes.length == 1 ? expressionNodes[0] : undefined
|
|
409
751
|
|
|
410
|
-
if (
|
|
752
|
+
if (expressionNodes.length > 1) {
|
|
411
753
|
const unsupported = expressionNodes[0]
|
|
412
754
|
|
|
413
755
|
return unsupportedSyntax(
|
|
414
756
|
"java",
|
|
415
|
-
unsupported?.name ?? "
|
|
757
|
+
unsupported?.name ?? "return expression",
|
|
416
758
|
unsupported ? nodeLocation(unsupported, filename, source) : location
|
|
417
759
|
)
|
|
418
760
|
}
|
|
419
761
|
|
|
420
|
-
return {
|
|
762
|
+
return {
|
|
763
|
+
...(expression ? {expression: convertExpression(
|
|
764
|
+
expression,
|
|
765
|
+
filename,
|
|
766
|
+
source,
|
|
767
|
+
context,
|
|
768
|
+
context.returnType?.kind == "TypeReference" && context.returnType.name == "void"
|
|
769
|
+
? undefined
|
|
770
|
+
: /** @type {import("../semantic/types.js").SemanticValueType | undefined} */ (context.returnType)
|
|
771
|
+
)} : {}),
|
|
772
|
+
kind: "ReturnStatement",
|
|
773
|
+
location
|
|
774
|
+
}
|
|
421
775
|
}
|
|
422
776
|
|
|
423
777
|
/**
|
|
@@ -425,9 +779,10 @@ function convertReturn(statement, filename, source) {
|
|
|
425
779
|
* @param {import("@lezer/common").SyntaxNode} statement - Java statement.
|
|
426
780
|
* @param {string} filename - Source filename.
|
|
427
781
|
* @param {string} source - Complete source.
|
|
428
|
-
* @
|
|
782
|
+
* @param {JavaConversionContext} context - Typed lexical conversion context.
|
|
783
|
+
* @returns {import("../semantic/types.js").LocalStatement | import("../semantic/types.js").PrivateFieldWriteStatement} Semantic local or private-field statement.
|
|
429
784
|
*/
|
|
430
|
-
function convertLocalStatement(statement, filename, source) {
|
|
785
|
+
function convertLocalStatement(statement, filename, source, context) {
|
|
431
786
|
const location = nodeLocation(statement, filename, source)
|
|
432
787
|
|
|
433
788
|
if (statement.name == "LocalVariableDeclaration") {
|
|
@@ -457,13 +812,21 @@ function convertLocalStatement(statement, filename, source) {
|
|
|
457
812
|
|
|
458
813
|
const name = nodeText(definition, source)
|
|
459
814
|
|
|
815
|
+
if (context.erasedBindingNames.has(name)) {
|
|
816
|
+
return unsupportedSyntax("java", "local binding collides with a consumed source binding", nodeLocation(definition, filename, source))
|
|
817
|
+
}
|
|
818
|
+
const type = convertType(typeNode, `Local '${name}'`, location, filename, source, context.recordNames, context.typeParameters)
|
|
819
|
+
const initializer = convertExpression(initializerNodes[0], filename, source, context, type)
|
|
820
|
+
|
|
821
|
+
context.bindings.set(name, type)
|
|
822
|
+
|
|
460
823
|
return withParserRanges({
|
|
461
|
-
initializer
|
|
824
|
+
initializer,
|
|
462
825
|
kind: "LocalDeclaration",
|
|
463
826
|
location,
|
|
464
827
|
mutable: modifierChildren.length == 0,
|
|
465
828
|
name,
|
|
466
|
-
type
|
|
829
|
+
type
|
|
467
830
|
}, {name: nodeLocation(definition, filename, source), operator: nodeLocation(assignment, filename, source)})
|
|
468
831
|
}
|
|
469
832
|
|
|
@@ -478,6 +841,23 @@ function convertLocalStatement(statement, filename, source) {
|
|
|
478
841
|
const expression = children.at(-1)
|
|
479
842
|
|
|
480
843
|
if (!operator || nodeText(operator, source) != "=") return unsupportedSyntax("java", "compound assignment", nodeLocation(assignment, filename, source))
|
|
844
|
+
if (target?.name == "FieldAccess" && context.currentClass) {
|
|
845
|
+
const targetChildren = structuralChildren(target)
|
|
846
|
+
const receiver = targetChildren[0]
|
|
847
|
+
const member = targetChildren[1]
|
|
848
|
+
const field = targetChildren.length == 2 && receiver?.name == "this" && member?.name == "Identifier"
|
|
849
|
+
? context.currentClass.fields.find((candidate) => candidate.name == nodeText(member, source))
|
|
850
|
+
: undefined
|
|
851
|
+
|
|
852
|
+
if (!field || !expression || expression == target || expression == operator) {
|
|
853
|
+
return unsupportedSyntax("java", "private field write outside the declaring receiver", nodeLocation(target, filename, source))
|
|
854
|
+
}
|
|
855
|
+
return withParserRanges({expression: convertExpression(expression, filename, source, context, field.type),
|
|
856
|
+
field: /** @type {string} */ (field.id), kind: /** @type {const} */ ("PrivateFieldWriteStatement"), location,
|
|
857
|
+
receiver: /** @type {import("../semantic/types.js").ReceiverExpression} */ (
|
|
858
|
+
convertExpression(receiver, filename, source, context))},
|
|
859
|
+
{member: nodeLocation(member, filename, source), operator: nodeLocation(operator, filename, source)})
|
|
860
|
+
}
|
|
481
861
|
if (!target || target.name != "Identifier") return unsupportedSyntax("java", target?.name ?? "assignment target", target ? nodeLocation(target, filename, source) : location)
|
|
482
862
|
if (!expression || expression == target || expression == operator) return unsupportedSyntax("java", "assignment expression", nodeLocation(assignment, filename, source))
|
|
483
863
|
|
|
@@ -487,8 +867,13 @@ function convertLocalStatement(statement, filename, source) {
|
|
|
487
867
|
name: nodeText(target, source)
|
|
488
868
|
}, {name: nodeLocation(target, filename, source)})
|
|
489
869
|
|
|
870
|
+
const targetName = nodeText(target, source)
|
|
871
|
+
const bindingType = context.bindings.get(targetName)
|
|
872
|
+
const value = convertExpression(expression, filename, source, context,
|
|
873
|
+
bindingType?.kind == "ErrorType" ? undefined : bindingType)
|
|
874
|
+
|
|
490
875
|
return withParserRanges({
|
|
491
|
-
expression:
|
|
876
|
+
expression: value,
|
|
492
877
|
kind: "AssignmentStatement",
|
|
493
878
|
location,
|
|
494
879
|
target: targetExpression
|
|
@@ -503,38 +888,618 @@ function convertLocalStatement(statement, filename, source) {
|
|
|
503
888
|
* @param {import("@lezer/common").SyntaxNode} statement - Java statement.
|
|
504
889
|
* @param {string} filename - Source filename.
|
|
505
890
|
* @param {string} source - Complete source.
|
|
891
|
+
* @param {JavaConversionContext} context - Typed lexical conversion context.
|
|
506
892
|
* @returns {import("../semantic/types.js").Statement} Semantic statement.
|
|
507
893
|
*/
|
|
508
|
-
function convertStatement(statement, filename, source) {
|
|
509
|
-
if (statement.name == "ReturnStatement") return convertReturn(statement, filename, source)
|
|
510
|
-
if (statement.name == "IfStatement") return convertIf(statement, filename, source)
|
|
511
|
-
if (statement.name == "
|
|
894
|
+
function convertStatement(statement, filename, source, context) {
|
|
895
|
+
if (statement.name == "ReturnStatement") return convertReturn(statement, filename, source, context)
|
|
896
|
+
if (statement.name == "IfStatement") return convertIf(statement, filename, source, context)
|
|
897
|
+
if (statement.name == "ThrowStatement") return convertJavaRaise(statement, filename, source, context)
|
|
898
|
+
if (statement.name == "TryStatement") return convertJavaTry(statement, filename, source, context)
|
|
899
|
+
if (statement.name == "WhileStatement") return convertWhile(statement, filename, source, context)
|
|
900
|
+
if (statement.name == "EnhancedForStatement") return convertForEach(statement, filename, source, context)
|
|
901
|
+
if (statement.name == "BreakStatement" || statement.name == "ContinueStatement") {
|
|
902
|
+
const label = statement.getChild("Label")
|
|
903
|
+
|
|
904
|
+
if (label) return unsupportedSyntax("java", `labeled ${statement.name == "BreakStatement" ? "break" : "continue"}`,
|
|
905
|
+
nodeLocation(label, filename, source))
|
|
906
|
+
const keyword = requiredChild(statement, statement.name == "BreakStatement" ? "break" : "continue", filename, source)
|
|
907
|
+
const location = nodeLocation(keyword, filename, source)
|
|
908
|
+
|
|
909
|
+
return withParserRanges({
|
|
910
|
+
kind: /** @type {"BreakStatement" | "ContinueStatement"} */ (
|
|
911
|
+
statement.name == "BreakStatement" ? "BreakStatement" : "ContinueStatement"
|
|
912
|
+
),
|
|
913
|
+
location
|
|
914
|
+
}, {keyword: location})
|
|
915
|
+
}
|
|
916
|
+
if (statement.name == "LocalVariableDeclaration") return convertLocalStatement(statement, filename, source, context)
|
|
512
917
|
if (statement.name == "ExpressionStatement") {
|
|
513
|
-
if (statement.getChild("AssignmentExpression")) return convertLocalStatement(statement, filename, source)
|
|
514
|
-
|
|
918
|
+
if (statement.getChild("AssignmentExpression")) return convertLocalStatement(statement, filename, source, context)
|
|
919
|
+
const invocation = statement.getChild("MethodInvocation")
|
|
920
|
+
|
|
921
|
+
if (!invocation) {
|
|
515
922
|
return unsupportedSyntax("java", statement.name, nodeLocation(statement, filename, source))
|
|
516
923
|
}
|
|
924
|
+
const fieldAccess = invocation.getChild("FieldAccess")
|
|
925
|
+
const methodName = invocation.getChild("MethodName")
|
|
926
|
+
|
|
927
|
+
if (!fieldAccess || !methodName || nodeText(fieldAccess, source) != "System.out" || nodeText(methodName, source) != "println") {
|
|
928
|
+
return {
|
|
929
|
+
expression: /** @type {import("../semantic/types.js").CallExpression | import("../semantic/types.js").MethodCallExpression} */ (
|
|
930
|
+
convertExpression(invocation, filename, source, context)
|
|
931
|
+
),
|
|
932
|
+
kind: "ExpressionStatement",
|
|
933
|
+
location: nodeLocation(statement, filename, source)
|
|
934
|
+
}
|
|
935
|
+
}
|
|
517
936
|
|
|
518
|
-
return convertPrint(statement, filename, source)
|
|
937
|
+
return convertPrint(statement, filename, source, context)
|
|
519
938
|
}
|
|
520
939
|
|
|
521
940
|
return unsupportedSyntax("java", statement.name, nodeLocation(statement, filename, source))
|
|
522
941
|
}
|
|
523
942
|
|
|
943
|
+
/**
|
|
944
|
+
* Converts exact `throw new DeclaredError(message)`.
|
|
945
|
+
* @param {import("@lezer/common").SyntaxNode} statement - Java throw statement.
|
|
946
|
+
* @param {string} filename - Source filename.
|
|
947
|
+
* @param {string} source - Complete source.
|
|
948
|
+
* @param {JavaConversionContext} context - Typed lexical context.
|
|
949
|
+
* @returns {import("../semantic/types.js").RaiseStatement} Semantic raise.
|
|
950
|
+
*/
|
|
951
|
+
function convertJavaRaise(statement, filename, source, context) {
|
|
952
|
+
const location = nodeLocation(statement, filename, source)
|
|
953
|
+
const construction = statement.getChild("ObjectCreationExpression")
|
|
954
|
+
const typeNode = construction?.getChild("TypeName")
|
|
955
|
+
const argumentsNode = construction?.getChild("ArgumentList")
|
|
956
|
+
const arguments_ = argumentsNode ? structuralChildren(argumentsNode) : []
|
|
957
|
+
const declaration = typeNode ? context.errorNames.get(nodeText(typeNode, source)) : undefined
|
|
958
|
+
|
|
959
|
+
if (!construction || !typeNode || !argumentsNode || !declaration || arguments_.length != 1 || construction.getChild("ClassBody")) {
|
|
960
|
+
return unsupportedSyntax("java", "throw other than exact declared error construction", location)
|
|
961
|
+
}
|
|
962
|
+
const typeLocation = nodeLocation(typeNode, filename, source)
|
|
963
|
+
|
|
964
|
+
return withParserRanges({
|
|
965
|
+
error: withParserRanges({
|
|
966
|
+
error: withParserRanges({declarationId: /** @type {string} */ (declaration.id), kind: /** @type {const} */ ("ErrorType")}, {type: typeLocation}),
|
|
967
|
+
kind: /** @type {const} */ ("ErrorConstruction"),
|
|
968
|
+
location: nodeLocation(construction, filename, source),
|
|
969
|
+
message: convertExpression(arguments_[0], filename, source, context)
|
|
970
|
+
}, {type: typeLocation}),
|
|
971
|
+
kind: /** @type {const} */ ("RaiseStatement"),
|
|
972
|
+
location
|
|
973
|
+
}, {keyword: nodeLocation(requiredChild(statement, "throw", filename, source), filename, source)})
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
/**
|
|
977
|
+
* Converts one exact catch with no resources or finally.
|
|
978
|
+
* @param {import("@lezer/common").SyntaxNode} statement - Java try statement.
|
|
979
|
+
* @param {string} filename - Source filename.
|
|
980
|
+
* @param {string} source - Complete source.
|
|
981
|
+
* @param {JavaConversionContext} context - Typed lexical context.
|
|
982
|
+
* @returns {import("../semantic/types.js").TryStatement} Semantic exact handler.
|
|
983
|
+
*/
|
|
984
|
+
function convertJavaTry(statement, filename, source, context) {
|
|
985
|
+
const location = nodeLocation(statement, filename, source)
|
|
986
|
+
const body = statement.getChild("Block")
|
|
987
|
+
const catches = statement.getChildren("CatchClause")
|
|
988
|
+
|
|
989
|
+
if (!body || catches.length != 1 || statement.getChild("FinallyClause") || statement.getChild("ResourceSpecification")) {
|
|
990
|
+
return unsupportedSyntax("java", "try without one catch and no resources or finally", location)
|
|
991
|
+
}
|
|
992
|
+
const handler = catches[0]
|
|
993
|
+
const parameter = handler.getChild("CatchFormalParameter")
|
|
994
|
+
const caught = parameter?.getChild("CatchType")
|
|
995
|
+
const typeNodes = caught ? structuralChildren(caught) : []
|
|
996
|
+
const definition = parameter?.getChild("Definition")
|
|
997
|
+
const catchBody = handler.getChild("Block")
|
|
998
|
+
const typeNode = typeNodes[0]
|
|
999
|
+
const declaration = typeNode ? context.errorNames.get(nodeText(typeNode, source)) : undefined
|
|
1000
|
+
|
|
1001
|
+
if (!parameter || !caught || typeNodes.length != 1 || typeNode?.name != "TypeName" || !definition || !catchBody || !declaration ||
|
|
1002
|
+
parameter.getChild("Modifiers")) {
|
|
1003
|
+
return unsupportedSyntax("java", "broad, multiple, or malformed catch", nodeLocation(handler, filename, source))
|
|
1004
|
+
}
|
|
1005
|
+
const caughtLocation = nodeLocation(typeNode, filename, source)
|
|
1006
|
+
const catchType = withParserRanges({declarationId: /** @type {string} */ (declaration.id), kind: /** @type {const} */ ("ErrorType")}, {
|
|
1007
|
+
type: caughtLocation
|
|
1008
|
+
})
|
|
1009
|
+
const bodyContext = {...context, bindings: new Map(context.bindings), erasedBindingNames: new Set(context.erasedBindingNames)}
|
|
1010
|
+
const catchContext = {...context, bindings: new Map(context.bindings), erasedBindingNames: new Set(context.erasedBindingNames)}
|
|
1011
|
+
const name = nodeText(definition, source)
|
|
1012
|
+
|
|
1013
|
+
catchContext.bindings.set(name, catchType)
|
|
1014
|
+
return withParserRanges({
|
|
1015
|
+
body: convertBlock(body, filename, source, bodyContext),
|
|
1016
|
+
catchBinding: withParserRanges({
|
|
1017
|
+
kind: /** @type {const} */ ("CatchBinding"),
|
|
1018
|
+
location: nodeLocation(definition, filename, source),
|
|
1019
|
+
mutable: /** @type {const} */ (false),
|
|
1020
|
+
name,
|
|
1021
|
+
type: catchType
|
|
1022
|
+
}, {name: nodeLocation(definition, filename, source)}),
|
|
1023
|
+
catchBody: convertBlock(catchBody, filename, source, catchContext),
|
|
1024
|
+
catchType,
|
|
1025
|
+
kind: /** @type {const} */ ("TryStatement"),
|
|
1026
|
+
location
|
|
1027
|
+
}, {catch: nodeLocation(handler, filename, source), try: nodeLocation(requiredChild(statement, "try", filename, source), filename, source)})
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
/**
|
|
1031
|
+
* Converts exact Java enhanced-for syntax over one resolved `List<T>`.
|
|
1032
|
+
* @param {import("@lezer/common").SyntaxNode} node - Enhanced-for statement.
|
|
1033
|
+
* @param {string} filename - Source filename.
|
|
1034
|
+
* @param {string} source - Complete source.
|
|
1035
|
+
* @param {JavaConversionContext} context - Typed lexical context.
|
|
1036
|
+
* @returns {import("../semantic/types.js").ForEachStatement | import("../semantic/types.js").ForEachMapStatement} Semantic loop.
|
|
1037
|
+
*/
|
|
1038
|
+
function convertForEach(node, filename, source, context) {
|
|
1039
|
+
const location = nodeLocation(node, filename, source)
|
|
1040
|
+
const spec = node.getChild("ForSpec")
|
|
1041
|
+
const body = directChildren(node).findLast((child) => child.name != "ForSpec" && child.name != "for")
|
|
1042
|
+
|
|
1043
|
+
if (!spec) return unsupportedSyntax("java", "enhanced for without specification", location)
|
|
1044
|
+
if (!body || body.name != "Block") return unsupportedSyntax("java", "enhanced for without block body", body ? nodeLocation(body, filename, source) : location)
|
|
1045
|
+
const children = directChildren(spec)
|
|
1046
|
+
const colonIndex = children.findIndex((child) => child.name == ":")
|
|
1047
|
+
const typeNode = colonIndex >= 2 ? children[colonIndex - 2] : undefined
|
|
1048
|
+
const bindingNode = colonIndex >= 2 ? children[colonIndex - 1] : undefined
|
|
1049
|
+
const collectionNode = colonIndex >= 0 ? children[colonIndex + 1] : undefined
|
|
1050
|
+
|
|
1051
|
+
if (!typeNode || !bindingNode || bindingNode.name != "Definition" || !collectionNode ||
|
|
1052
|
+
children.filter((child) => child.name == ":").length != 1) {
|
|
1053
|
+
const unsupported = bindingNode ?? collectionNode ?? spec
|
|
1054
|
+
|
|
1055
|
+
return unsupportedSyntax("java", "malformed enhanced for specification", nodeLocation(unsupported, filename, source))
|
|
1056
|
+
}
|
|
1057
|
+
if (children.some((child) => child.name == "Modifiers")) {
|
|
1058
|
+
const modifiers = /** @type {import("@lezer/common").SyntaxNode} */ (children.find((child) => child.name == "Modifiers"))
|
|
1059
|
+
|
|
1060
|
+
return unsupportedSyntax("java", "enhanced for modifiers", nodeLocation(modifiers, filename, source))
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
if (collectionNode.name == "MethodInvocation") {
|
|
1064
|
+
const methodName = collectionNode.getChild("MethodName")
|
|
1065
|
+
const argumentList = collectionNode.getChild("ArgumentList")
|
|
1066
|
+
const mapNode = structuralChildren(collectionNode).find((child) => child.name != "MethodName" && child.name != "ArgumentList")
|
|
1067
|
+
const mapType_ = mapNode ? knownExpressionType(mapNode, filename, source, context) : undefined
|
|
1068
|
+
|
|
1069
|
+
if (methodName && argumentList && mapNode?.name == "Identifier" &&
|
|
1070
|
+
nodeText(methodName, source) == "sequencedEntrySet" && structuralChildren(argumentList).length == 0 &&
|
|
1071
|
+
mapType_?.kind == "OrderedMapType") {
|
|
1072
|
+
const entryType = typeNode.name == "GenericType" ? structuralChildren(typeNode) : []
|
|
1073
|
+
const entryArguments = entryType[1]?.name == "TypeArguments" ? structuralChildren(entryType[1]) : []
|
|
1074
|
+
const parsedKeyType = entryArguments[0]
|
|
1075
|
+
? convertJavaTypeArgument(entryArguments[0], "Map entry key", nodeLocation(typeNode, filename, source), filename, source,
|
|
1076
|
+
context.recordNames, context.typeParameters)
|
|
1077
|
+
: undefined
|
|
1078
|
+
const parsedValueType = entryArguments[1]
|
|
1079
|
+
? convertJavaTypeArgument(entryArguments[1], "Map entry value", nodeLocation(typeNode, filename, source), filename, source,
|
|
1080
|
+
context.recordNames, context.typeParameters)
|
|
1081
|
+
: undefined
|
|
1082
|
+
|
|
1083
|
+
if (entryType.length != 2 || nodeText(entryType[0], source) != "java.util.Map.Entry" || entryArguments.length != 2 ||
|
|
1084
|
+
!parsedKeyType || !parsedValueType || !sameValueType(parsedKeyType, mapType_.keyType) ||
|
|
1085
|
+
!sameValueType(parsedValueType, mapType_.valueType)) {
|
|
1086
|
+
return unsupportedSyntax("java", "ordered-map entry type", nodeLocation(typeNode, filename, source))
|
|
1087
|
+
}
|
|
1088
|
+
const bodyStatements = structuralChildren(body)
|
|
1089
|
+
|
|
1090
|
+
if (bodyStatements.length < 2) {
|
|
1091
|
+
return unsupportedSyntax("java", "ordered-map pair bindings", nodeLocation(body, filename, source))
|
|
1092
|
+
}
|
|
1093
|
+
const entryName = nodeText(bindingNode, source)
|
|
1094
|
+
|
|
1095
|
+
if (context.bindings.has(entryName) || context.erasedBindingNames.has(entryName)) {
|
|
1096
|
+
return unsupportedSyntax("java", "ordered-map entry binding collision", nodeLocation(bindingNode, filename, source))
|
|
1097
|
+
}
|
|
1098
|
+
const bodyContext = {
|
|
1099
|
+
...context,
|
|
1100
|
+
bindings: new Map(context.bindings),
|
|
1101
|
+
erasedBindingNames: new Set(context.erasedBindingNames).add(entryName)
|
|
1102
|
+
}
|
|
1103
|
+
const keyBinding = convertJavaMapPairBinding(
|
|
1104
|
+
bodyStatements[0], entryName, "getKey", mapType_.keyType, filename, source, bodyContext
|
|
1105
|
+
)
|
|
1106
|
+
const valueBinding = convertJavaMapPairBinding(
|
|
1107
|
+
bodyStatements[1], entryName, "getValue", mapType_.valueType, filename, source, bodyContext
|
|
1108
|
+
)
|
|
1109
|
+
|
|
1110
|
+
bodyContext.bindings.set(keyBinding.name, keyBinding.type)
|
|
1111
|
+
bodyContext.bindings.set(valueBinding.name, valueBinding.type)
|
|
1112
|
+
return withParserRanges({
|
|
1113
|
+
body: {
|
|
1114
|
+
kind: /** @type {const} */ ("Block"),
|
|
1115
|
+
location: nodeLocation(body, filename, source),
|
|
1116
|
+
statements: convertStatements(bodyStatements.slice(2), filename, source, bodyContext)
|
|
1117
|
+
},
|
|
1118
|
+
keyBinding,
|
|
1119
|
+
kind: /** @type {const} */ ("ForEachMapStatement"),
|
|
1120
|
+
location,
|
|
1121
|
+
map: convertExpression(mapNode, filename, source, context),
|
|
1122
|
+
valueBinding
|
|
1123
|
+
}, {operator: nodeLocation(children[colonIndex], filename, source)})
|
|
1124
|
+
}
|
|
1125
|
+
if (methodName && ["entrySet", "sequencedEntrySet"].includes(nodeText(methodName, source))) {
|
|
1126
|
+
return unsupportedSyntax("java", "unordered or unsupported map iteration", nodeLocation(collectionNode, filename, source))
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
const bindingLocation = nodeLocation(bindingNode, filename, source)
|
|
1130
|
+
const declaredType = convertJavaTypeArgument(typeNode, `Iteration binding '${nodeText(bindingNode, source)}'`, bindingLocation,
|
|
1131
|
+
filename, source, context.recordNames, context.typeParameters)
|
|
1132
|
+
const collectionType = iterationOperandType(knownExpressionType(collectionNode, filename, source, context))
|
|
1133
|
+
|
|
1134
|
+
if (!collectionType || collectionType.kind != "ListType" && collectionType.kind != "MapType") {
|
|
1135
|
+
return missingType("java", "Iteration collection", nodeLocation(collectionNode, filename, source))
|
|
1136
|
+
}
|
|
1137
|
+
const valueBinding = withParserRanges({
|
|
1138
|
+
kind: /** @type {const} */ ("ValueBinding"),
|
|
1139
|
+
location: bindingLocation,
|
|
1140
|
+
mutable: /** @type {const} */ (false),
|
|
1141
|
+
name: nodeText(bindingNode, source),
|
|
1142
|
+
type: declaredType
|
|
1143
|
+
}, {name: bindingLocation})
|
|
1144
|
+
const bodyContext = {...context, bindings: new Map(context.bindings), erasedBindingNames: new Set(context.erasedBindingNames)}
|
|
1145
|
+
|
|
1146
|
+
bodyContext.bindings.set(valueBinding.name, iterationBindingType(declaredType, bindingLocation))
|
|
1147
|
+
return withParserRanges({
|
|
1148
|
+
body: convertBlock(body, filename, source, bodyContext),
|
|
1149
|
+
kind: /** @type {const} */ ("ForEachStatement"),
|
|
1150
|
+
list: convertExpression(collectionNode, filename, source, context),
|
|
1151
|
+
location,
|
|
1152
|
+
valueBinding
|
|
1153
|
+
}, {operator: nodeLocation(children[colonIndex], filename, source)})
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
/**
|
|
1157
|
+
* Converts one exact immutable local extracted from a Java ordered-map entry.
|
|
1158
|
+
* @param {import("@lezer/common").SyntaxNode} statement - Pair-binding declaration.
|
|
1159
|
+
* @param {string} entryName - Enhanced-for entry binding name.
|
|
1160
|
+
* @param {"getKey" | "getValue"} accessor - Required entry accessor.
|
|
1161
|
+
* @param {import("../semantic/types.js").SemanticValueType} expectedType - Canonical binding type.
|
|
1162
|
+
* @param {string} filename - Source filename.
|
|
1163
|
+
* @param {string} source - Complete source.
|
|
1164
|
+
* @param {JavaConversionContext} context - Typed context.
|
|
1165
|
+
* @returns {import("../semantic/types.js").ValueBinding} Pair binding.
|
|
1166
|
+
*/
|
|
1167
|
+
function convertJavaMapPairBinding(statement, entryName, accessor, expectedType, filename, source, context) {
|
|
1168
|
+
const location = nodeLocation(statement, filename, source)
|
|
1169
|
+
const modifiers = statement.getChild("Modifiers")
|
|
1170
|
+
const declarators = statement.getChildren("VariableDeclarator")
|
|
1171
|
+
const declarator = declarators[0]
|
|
1172
|
+
const definition = declarator?.getChild("Definition")
|
|
1173
|
+
const assignment = declarator?.getChild("AssignOp")
|
|
1174
|
+
const initializer = declarator
|
|
1175
|
+
? directChildren(declarator).find((child) => child.name != "Definition" && child.name != "AssignOp")
|
|
1176
|
+
: undefined
|
|
1177
|
+
const methodName = initializer?.getChild("MethodName")
|
|
1178
|
+
const argumentList = initializer?.getChild("ArgumentList")
|
|
1179
|
+
const receiver = initializer?.name == "MethodInvocation"
|
|
1180
|
+
? structuralChildren(initializer).find((child) => child.name != "MethodName" && child.name != "ArgumentList")
|
|
1181
|
+
: undefined
|
|
1182
|
+
const typeNode = declarationType(statement)
|
|
1183
|
+
|
|
1184
|
+
if (statement.name != "LocalVariableDeclaration" || nodeText(modifiers ?? statement, source) != "final" ||
|
|
1185
|
+
declarators.length != 1 || !definition || !assignment || nodeText(assignment, source) != "=" || !initializer ||
|
|
1186
|
+
initializer.name != "MethodInvocation" || !methodName || nodeText(methodName, source) != accessor ||
|
|
1187
|
+
!argumentList || structuralChildren(argumentList).length != 0 || receiver?.name != "Identifier" ||
|
|
1188
|
+
nodeText(receiver, source) != entryName || !typeNode) {
|
|
1189
|
+
return unsupportedSyntax("java", `ordered-map ${accessor} binding`, location)
|
|
1190
|
+
}
|
|
1191
|
+
const name = nodeText(definition, source)
|
|
1192
|
+
|
|
1193
|
+
if (context.erasedBindingNames.has(name)) {
|
|
1194
|
+
return unsupportedSyntax("java", `ordered-map ${accessor} binding collision`, nodeLocation(definition, filename, source))
|
|
1195
|
+
}
|
|
1196
|
+
const type = convertType(typeNode, `Map iteration binding '${name}'`, location,
|
|
1197
|
+
filename, source, context.recordNames, context.typeParameters)
|
|
1198
|
+
|
|
1199
|
+
if (!sameValueType(type, expectedType)) {
|
|
1200
|
+
return unsupportedSyntax("java", `ordered-map ${accessor} binding type`, nodeLocation(typeNode, filename, source))
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
return withParserRanges({
|
|
1204
|
+
kind: /** @type {const} */ ("ValueBinding"),
|
|
1205
|
+
location,
|
|
1206
|
+
mutable: /** @type {const} */ (false),
|
|
1207
|
+
name,
|
|
1208
|
+
type
|
|
1209
|
+
}, {name: nodeLocation(definition, filename, source)})
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
/**
|
|
1213
|
+
* Resolves expression types established by Java declarations and function signatures.
|
|
1214
|
+
* @param {import("@lezer/common").SyntaxNode} node - Parser expression.
|
|
1215
|
+
* @param {string} filename - Source filename.
|
|
1216
|
+
* @param {string} source - Complete source.
|
|
1217
|
+
* @param {JavaConversionContext} context - Typed context.
|
|
1218
|
+
* @returns {import("../semantic/types.js").SemanticFunctionReturnType | import("../semantic/types.js").ErrorType | undefined} Known type.
|
|
1219
|
+
*/
|
|
1220
|
+
function knownExpressionType(node, filename, source, context) {
|
|
1221
|
+
if (node.name == "ParenthesizedExpression") {
|
|
1222
|
+
const children = structuralChildren(node)
|
|
1223
|
+
|
|
1224
|
+
return children.length == 1 ? knownExpressionType(children[0], filename, source, context) : undefined
|
|
1225
|
+
}
|
|
1226
|
+
if (node.name == "this" && context.currentClass?.id) {
|
|
1227
|
+
return {declarationId: context.currentClass.id, kind: "ReferenceType"}
|
|
1228
|
+
}
|
|
1229
|
+
if (node.name == "Identifier") return context.bindings.get(nodeText(node, source))
|
|
1230
|
+
if (node.name == "FieldAccess" && context.currentClass) {
|
|
1231
|
+
const children = structuralChildren(node)
|
|
1232
|
+
|
|
1233
|
+
if (children.length == 2 && children[0].name == "this" && children[1].name == "Identifier") {
|
|
1234
|
+
return context.currentClass.fields.find((field) => field.name == nodeText(children[1], source))?.type
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
if (node.name == "MethodInvocation") {
|
|
1238
|
+
const methodName = node.getChild("MethodName")
|
|
1239
|
+
const argumentList = node.getChild("ArgumentList")
|
|
1240
|
+
const receiver = structuralChildren(node).find((child) => child.name != "MethodName" && child.name != "ArgumentList")
|
|
1241
|
+
|
|
1242
|
+
if (methodName && !receiver) {
|
|
1243
|
+
const signature = context.functions.get(nodeText(methodName, source))
|
|
1244
|
+
const arguments_ = argumentList ? structuralChildren(argumentList) : []
|
|
1245
|
+
|
|
1246
|
+
if (signature) return knownCallReturnType(signature, arguments_.map((argument) =>
|
|
1247
|
+
knownExpressionType(argument, filename, source, context)))
|
|
1248
|
+
}
|
|
1249
|
+
if (methodName && receiver) {
|
|
1250
|
+
const receiverType = knownExpressionType(receiver, filename, source, context)
|
|
1251
|
+
|
|
1252
|
+
if (receiverType?.kind == "ReferenceType") {
|
|
1253
|
+
return context.classes.get(receiverType.declarationId)?.methods.find((method) =>
|
|
1254
|
+
method.name == nodeText(methodName, source))?.returnType
|
|
1255
|
+
}
|
|
1256
|
+
const qualified = `${nodeText(receiver, source)}.${nodeText(methodName, source)}`
|
|
1257
|
+
const signature = context.functions.get(qualified)
|
|
1258
|
+
const arguments_ = argumentList ? structuralChildren(argumentList) : []
|
|
1259
|
+
const returnType = signature ? knownCallReturnType(signature, arguments_.map((argument) =>
|
|
1260
|
+
knownExpressionType(argument, filename, source, context))) : undefined
|
|
1261
|
+
|
|
1262
|
+
if (returnType) return returnType
|
|
1263
|
+
}
|
|
1264
|
+
if (methodName && receiver && argumentList && nodeText(methodName, source) == "get") {
|
|
1265
|
+
const arguments_ = structuralChildren(argumentList)
|
|
1266
|
+
const receiverType = knownExpressionType(receiver, filename, source, context)
|
|
1267
|
+
|
|
1268
|
+
if (arguments_.length == 0 && receiver.name == "Identifier" && receiverType?.kind == "OptionalType") {
|
|
1269
|
+
return receiverType.valueType
|
|
1270
|
+
}
|
|
1271
|
+
if (arguments_.length == 1 && receiverType?.kind == "ListType") return receiverType.elementType
|
|
1272
|
+
if (arguments_.length == 1 && (receiverType?.kind == "MapType" || receiverType?.kind == "OrderedMapType")) {
|
|
1273
|
+
return receiverType.valueType
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
if (methodName && receiver && argumentList && structuralChildren(argumentList).length == 0) {
|
|
1277
|
+
const receiverType = knownExpressionType(receiver, filename, source, context)
|
|
1278
|
+
|
|
1279
|
+
if (receiverType?.kind != "RecordType") return undefined
|
|
1280
|
+
const declaration = context.records.get(receiverType.declarationId)
|
|
1281
|
+
const index = declaration?.fields.findIndex((field) => field.name == nodeText(methodName, source)) ?? -1
|
|
1282
|
+
|
|
1283
|
+
return declaration ? instantiatedRecordFieldType(declaration, receiverType.arguments, index) : undefined
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
if (node.name == "ObjectCreationExpression") {
|
|
1287
|
+
const typeNode = structuralChildren(node).find((child) => ["GenericType", "TypeName", "ScopedTypeName"].includes(child.name))
|
|
1288
|
+
const nameNode = typeNode?.name == "GenericType" ? structuralChildren(typeNode)[0] : typeNode
|
|
1289
|
+
const declaration = nameNode ? context.recordNames.get(nodeText(nameNode, source)) : undefined
|
|
1290
|
+
|
|
1291
|
+
if (declaration?.id && typeNode) {
|
|
1292
|
+
const typeArgumentsNode = typeNode?.getChild("TypeArguments")
|
|
1293
|
+
const typeArguments = typeArgumentsNode
|
|
1294
|
+
? structuralChildren(typeArgumentsNode).map((argument) => convertJavaTypeArgument(
|
|
1295
|
+
argument,
|
|
1296
|
+
`Record '${declaration.name}' application`,
|
|
1297
|
+
nodeLocation(typeNode, filename, source),
|
|
1298
|
+
filename,
|
|
1299
|
+
source,
|
|
1300
|
+
context.recordNames,
|
|
1301
|
+
context.typeParameters
|
|
1302
|
+
))
|
|
1303
|
+
: undefined
|
|
1304
|
+
|
|
1305
|
+
return declaration.kind == "ClassDeclaration"
|
|
1306
|
+
? {declarationId: declaration.id, kind: "ReferenceType"}
|
|
1307
|
+
: {declarationId: declaration.id, kind: "RecordType", ...(typeArguments ? {arguments: typeArguments} : {})}
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
return undefined
|
|
1312
|
+
}
|
|
1313
|
+
|
|
524
1314
|
/**
|
|
525
1315
|
* Converts direct structural children of one Java block in source order.
|
|
526
1316
|
* @param {import("@lezer/common").SyntaxNode} node - Java Block node.
|
|
527
1317
|
* @param {string} filename - Source filename.
|
|
528
1318
|
* @param {string} source - Complete source.
|
|
1319
|
+
* @param {JavaConversionContext} context - Typed lexical conversion context.
|
|
529
1320
|
* @returns {import("../semantic/types.js").Block} Semantic block.
|
|
530
1321
|
*/
|
|
531
|
-
function convertBlock(node, filename, source) {
|
|
532
|
-
if (node.name != "Block"
|
|
1322
|
+
function convertBlock(node, filename, source, context) {
|
|
1323
|
+
if (node.name != "Block" && node.name != "ConstructorBody") {
|
|
1324
|
+
return unsupportedSyntax("java", `expected block, received ${node.name}`, nodeLocation(node, filename, source))
|
|
1325
|
+
}
|
|
533
1326
|
|
|
534
1327
|
return {
|
|
535
1328
|
kind: "Block",
|
|
536
1329
|
location: nodeLocation(node, filename, source),
|
|
537
|
-
statements: structuralChildren(node)
|
|
1330
|
+
statements: convertStatements(structuralChildren(node), filename, source, context)
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
/**
|
|
1335
|
+
* Converts a Java statement sequence, folding the exact protected ordered-map construction profile.
|
|
1336
|
+
* @param {import("@lezer/common").SyntaxNode[]} statements - Direct block statements.
|
|
1337
|
+
* @param {string} filename - Source filename.
|
|
1338
|
+
* @param {string} source - Complete source.
|
|
1339
|
+
* @param {JavaConversionContext} context - Typed lexical conversion context.
|
|
1340
|
+
* @returns {import("../semantic/types.js").Statement[]} Semantic statements.
|
|
1341
|
+
*/
|
|
1342
|
+
function convertStatements(statements, filename, source, context) {
|
|
1343
|
+
const converted = []
|
|
1344
|
+
|
|
1345
|
+
for (let index = 0; index < statements.length;) {
|
|
1346
|
+
const ordered = convertJavaOrderedMapSequence(statements, index, filename, source, context)
|
|
1347
|
+
|
|
1348
|
+
if (ordered) {
|
|
1349
|
+
converted.push(ordered.statement)
|
|
1350
|
+
index = ordered.nextIndex
|
|
1351
|
+
} else {
|
|
1352
|
+
converted.push(convertStatement(statements[index], filename, source, context))
|
|
1353
|
+
index += 1
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
return converted
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
/**
|
|
1361
|
+
* Recognizes construction through a private LinkedHashMap backing followed by an unmodifiable SequencedMap boundary.
|
|
1362
|
+
* @param {import("@lezer/common").SyntaxNode[]} statements - Direct block statements.
|
|
1363
|
+
* @param {number} start - Candidate backing-declaration index.
|
|
1364
|
+
* @param {string} filename - Source filename.
|
|
1365
|
+
* @param {string} source - Complete source.
|
|
1366
|
+
* @param {JavaConversionContext} context - Typed lexical conversion context.
|
|
1367
|
+
* @returns {{statement: import("../semantic/types.js").LocalDeclaration, nextIndex: number} | undefined} Folded declaration.
|
|
1368
|
+
*/
|
|
1369
|
+
function convertJavaOrderedMapSequence(statements, start, filename, source, context) {
|
|
1370
|
+
const backingStatement = statements[start]
|
|
1371
|
+
const backingTypeNode = backingStatement?.name == "LocalVariableDeclaration" ? declarationType(backingStatement) : null
|
|
1372
|
+
const backingTypeChildren = backingTypeNode?.name == "GenericType" ? structuralChildren(backingTypeNode) : []
|
|
1373
|
+
|
|
1374
|
+
if (backingTypeChildren.length != 2 || nodeText(backingTypeChildren[0], source) != "java.util.LinkedHashMap") return undefined
|
|
1375
|
+
|
|
1376
|
+
const location = nodeLocation(backingStatement, filename, source)
|
|
1377
|
+
const backingModifiers = backingStatement.getChild("Modifiers")
|
|
1378
|
+
const backingDeclarators = backingStatement.getChildren("VariableDeclarator")
|
|
1379
|
+
const backingDeclarator = backingDeclarators[0]
|
|
1380
|
+
const backingDefinition = backingDeclarator?.getChild("Definition")
|
|
1381
|
+
const backingAssignment = backingDeclarator?.getChild("AssignOp")
|
|
1382
|
+
const backingInitializer = backingDeclarator
|
|
1383
|
+
? directChildren(backingDeclarator).find((child) => child.name != "Definition" && child.name != "AssignOp")
|
|
1384
|
+
: undefined
|
|
1385
|
+
const constructionType = backingInitializer?.name == "ObjectCreationExpression"
|
|
1386
|
+
? structuralChildren(backingInitializer).find((child) => child.name == "GenericType")
|
|
1387
|
+
: undefined
|
|
1388
|
+
const constructionTypeChildren = constructionType ? structuralChildren(constructionType) : []
|
|
1389
|
+
const constructionArguments = backingInitializer?.getChild("ArgumentList")
|
|
1390
|
+
const backingArgumentsNode = backingTypeChildren[1]
|
|
1391
|
+
const backingArguments = backingArgumentsNode?.name == "TypeArguments" ? structuralChildren(backingArgumentsNode) : []
|
|
1392
|
+
|
|
1393
|
+
if (nodeText(backingModifiers ?? backingStatement, source) != "final" || backingDeclarators.length != 1 ||
|
|
1394
|
+
!backingDefinition || !backingAssignment || nodeText(backingAssignment, source) != "=" || !backingInitializer ||
|
|
1395
|
+
backingInitializer.name != "ObjectCreationExpression" || backingInitializer.getChild("ClassBody") ||
|
|
1396
|
+
constructionTypeChildren.length != 2 || nodeText(constructionTypeChildren[0], source) != "java.util.LinkedHashMap" ||
|
|
1397
|
+
structuralChildren(constructionTypeChildren[1]).length != 0 || !constructionArguments ||
|
|
1398
|
+
structuralChildren(constructionArguments).length != 0 || backingArguments.length != 2) {
|
|
1399
|
+
return unsupportedSyntax("java", "ordered-map LinkedHashMap backing", location)
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
const backingName = nodeText(backingDefinition, source)
|
|
1403
|
+
|
|
1404
|
+
if (context.bindings.has(backingName) || context.erasedBindingNames.has(backingName)) {
|
|
1405
|
+
return unsupportedSyntax("java", "ordered-map backing binding collision", nodeLocation(backingDefinition, filename, source))
|
|
1406
|
+
}
|
|
1407
|
+
const keyType = convertJavaTypeArgument(backingArguments[0], "Ordered map key", location, filename, source,
|
|
1408
|
+
context.recordNames, context.typeParameters)
|
|
1409
|
+
const valueType = convertJavaTypeArgument(backingArguments[1], "Ordered map value", location, filename, source,
|
|
1410
|
+
context.recordNames, context.typeParameters)
|
|
1411
|
+
const entries = []
|
|
1412
|
+
let index = start + 1
|
|
1413
|
+
|
|
1414
|
+
while (index < statements.length) {
|
|
1415
|
+
const putStatement = statements[index]
|
|
1416
|
+
const invocation = putStatement.name == "ExpressionStatement" ? putStatement.getChild("MethodInvocation") : null
|
|
1417
|
+
const methodName = invocation?.getChild("MethodName")
|
|
1418
|
+
const argumentList = invocation?.getChild("ArgumentList")
|
|
1419
|
+
const receiver = invocation
|
|
1420
|
+
? structuralChildren(invocation).find((child) => child.name != "MethodName" && child.name != "ArgumentList")
|
|
1421
|
+
: undefined
|
|
1422
|
+
|
|
1423
|
+
if (!invocation || receiver?.name != "Identifier" || nodeText(receiver, source) != backingName ||
|
|
1424
|
+
!methodName || nodeText(methodName, source) != "put") break
|
|
1425
|
+
const arguments_ = argumentList ? structuralChildren(argumentList) : []
|
|
1426
|
+
|
|
1427
|
+
if (arguments_.length != 2 || arguments_[0].name != "StringLiteral") {
|
|
1428
|
+
return unsupportedSyntax("java", "ordered-map put", nodeLocation(putStatement, filename, source))
|
|
1429
|
+
}
|
|
1430
|
+
entries.push(withParserRanges({
|
|
1431
|
+
key: /** @type {import("../semantic/types.js").StringLiteral} */ (
|
|
1432
|
+
convertExpression(arguments_[0], filename, source, context, keyType)
|
|
1433
|
+
),
|
|
1434
|
+
kind: /** @type {const} */ ("MapEntry"),
|
|
1435
|
+
location: nodeLocation(invocation, filename, source),
|
|
1436
|
+
value: convertExpression(arguments_[1], filename, source, context, valueType)
|
|
1437
|
+
}, {operator: nodeLocation(methodName, filename, source)}))
|
|
1438
|
+
index += 1
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
const sealStatement = statements[index]
|
|
1442
|
+
const sealTypeNode = sealStatement?.name == "LocalVariableDeclaration" ? declarationType(sealStatement) : null
|
|
1443
|
+
const sealDeclarators = sealStatement?.name == "LocalVariableDeclaration" ? sealStatement.getChildren("VariableDeclarator") : []
|
|
1444
|
+
const sealDeclarator = sealDeclarators[0]
|
|
1445
|
+
const sealDefinition = sealDeclarator?.getChild("Definition")
|
|
1446
|
+
const sealAssignment = sealDeclarator?.getChild("AssignOp")
|
|
1447
|
+
const sealInitializer = sealDeclarator
|
|
1448
|
+
? directChildren(sealDeclarator).find((child) => child.name != "Definition" && child.name != "AssignOp")
|
|
1449
|
+
: undefined
|
|
1450
|
+
const sealMethod = sealInitializer?.getChild("MethodName")
|
|
1451
|
+
const sealArgumentsNode = sealInitializer?.getChild("ArgumentList")
|
|
1452
|
+
const sealArguments = sealArgumentsNode ? structuralChildren(sealArgumentsNode) : []
|
|
1453
|
+
const sealReceiver = sealInitializer?.name == "MethodInvocation"
|
|
1454
|
+
? structuralChildren(sealInitializer).find((child) => child.name != "MethodName" && child.name != "ArgumentList")
|
|
1455
|
+
: undefined
|
|
1456
|
+
|
|
1457
|
+
if (!sealStatement || nodeText(sealStatement.getChild("Modifiers") ?? sealStatement, source) != "final" ||
|
|
1458
|
+
!sealTypeNode || sealDeclarators.length != 1 || !sealDefinition || !sealAssignment || nodeText(sealAssignment, source) != "=" ||
|
|
1459
|
+
sealInitializer?.name != "MethodInvocation" || nodeText(sealReceiver ?? sealStatement, source) != "java.util.Collections" ||
|
|
1460
|
+
!sealMethod || nodeText(sealMethod, source) != "unmodifiableSequencedMap" || sealArguments.length != 1 ||
|
|
1461
|
+
sealArguments[0].name != "Identifier" || nodeText(sealArguments[0], source) != backingName) {
|
|
1462
|
+
return unsupportedSyntax("java", "ordered-map unmodifiable SequencedMap boundary", location)
|
|
1463
|
+
}
|
|
1464
|
+
const name = nodeText(sealDefinition, source)
|
|
1465
|
+
|
|
1466
|
+
if (name == backingName) {
|
|
1467
|
+
return unsupportedSyntax("java", "ordered-map boundary binding collision", nodeLocation(sealDefinition, filename, source))
|
|
1468
|
+
}
|
|
1469
|
+
const boundaryType = convertType(sealTypeNode, `Local '${name}'`, nodeLocation(sealStatement, filename, source),
|
|
1470
|
+
filename, source, context.recordNames, context.typeParameters)
|
|
1471
|
+
const sealTypeChildren = sealTypeNode.name == "GenericType" ? structuralChildren(sealTypeNode) : []
|
|
1472
|
+
const sealTypeArguments = sealTypeChildren[1]?.name == "TypeArguments" ? structuralChildren(sealTypeChildren[1]) : []
|
|
1473
|
+
|
|
1474
|
+
if (sealTypeChildren.length != 2 || nodeText(sealTypeChildren[0], source) != "java.util.SequencedMap" ||
|
|
1475
|
+
sealTypeArguments.length != 2 || boundaryType.kind != "MapType" || !sameValueType(boundaryType.keyType, keyType) ||
|
|
1476
|
+
!sameValueType(boundaryType.valueType, valueType)) {
|
|
1477
|
+
return unsupportedSyntax("java", "ordered-map boundary type mismatch", nodeLocation(sealTypeNode, filename, source))
|
|
1478
|
+
}
|
|
1479
|
+
const type = orderedMapType(
|
|
1480
|
+
boundaryType.keyType,
|
|
1481
|
+
boundaryType.valueType,
|
|
1482
|
+
nodeLocation(sealTypeNode, filename, source),
|
|
1483
|
+
nodeLocation(sealTypeArguments[0], filename, source),
|
|
1484
|
+
nodeLocation(sealTypeArguments[1], filename, source)
|
|
1485
|
+
)
|
|
1486
|
+
|
|
1487
|
+
context.bindings.set(name, type)
|
|
1488
|
+
context.erasedBindingNames.add(backingName)
|
|
1489
|
+
return {
|
|
1490
|
+
nextIndex: index + 1,
|
|
1491
|
+
statement: withParserRanges({
|
|
1492
|
+
initializer: withParserRanges({
|
|
1493
|
+
entries,
|
|
1494
|
+
kind: /** @type {const} */ ("OrderedMapLiteral"),
|
|
1495
|
+
location: locationFromOffsets(filename, source, backingInitializer.from, sealInitializer.to)
|
|
1496
|
+
}, {factory: nodeLocation(backingInitializer, filename, source)}),
|
|
1497
|
+
kind: /** @type {const} */ ("LocalDeclaration"),
|
|
1498
|
+
location: locationFromOffsets(filename, source, backingStatement.from, sealStatement.to),
|
|
1499
|
+
mutable: /** @type {const} */ (false),
|
|
1500
|
+
name,
|
|
1501
|
+
type
|
|
1502
|
+
}, {name: nodeLocation(sealDefinition, filename, source), operator: nodeLocation(sealAssignment, filename, source)})
|
|
538
1503
|
}
|
|
539
1504
|
}
|
|
540
1505
|
|
|
@@ -545,14 +1510,94 @@ function convertBlock(node, filename, source) {
|
|
|
545
1510
|
* @param {import("../semantic/types.js").SourceLocation} location - Source location.
|
|
546
1511
|
* @param {string} filename - Source filename.
|
|
547
1512
|
* @param {string} source - Complete source.
|
|
548
|
-
* @
|
|
1513
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} [recordNames] - Nominal declarations by source name.
|
|
1514
|
+
* @param {Map<string, import("../semantic/types.js").TypeParameter>} [typeParameters] - Declaration-scoped parameters.
|
|
1515
|
+
* @returns {import("../semantic/types.js").SemanticValueType} Semantic type.
|
|
549
1516
|
*/
|
|
550
|
-
function convertType(sourceType, subject, location, filename, source) {
|
|
1517
|
+
function convertType(sourceType, subject, location, filename, source, recordNames = new Map(), typeParameters = new Map()) {
|
|
551
1518
|
if (!sourceType) return missingType("java", subject, location)
|
|
1519
|
+
if (sourceType.name == "void") return unsupportedSyntax("java", "void value type", nodeLocation(sourceType, filename, source))
|
|
1520
|
+
if (sourceType.name == "GenericType") {
|
|
1521
|
+
const children = structuralChildren(sourceType)
|
|
1522
|
+
const nameNode = children[0]
|
|
1523
|
+
const argumentsNode = children[1]
|
|
1524
|
+
|
|
1525
|
+
if (!nameNode || !argumentsNode || argumentsNode.name != "TypeArguments") {
|
|
1526
|
+
return unsupportedSyntax("java", "malformed generic collection type", nodeLocation(sourceType, filename, source))
|
|
1527
|
+
}
|
|
1528
|
+
const name = nodeText(nameNode, source)
|
|
1529
|
+
const arguments_ = structuralChildren(argumentsNode)
|
|
1530
|
+
|
|
1531
|
+
if (name == "java.util.List" && arguments_.length == 1) {
|
|
1532
|
+
return listType(
|
|
1533
|
+
convertJavaTypeArgument(arguments_[0], subject, location, filename, source, recordNames, typeParameters),
|
|
1534
|
+
nodeLocation(sourceType, filename, source),
|
|
1535
|
+
nodeLocation(arguments_[0], filename, source)
|
|
1536
|
+
)
|
|
1537
|
+
}
|
|
1538
|
+
if (name == "java.util.Map" && arguments_.length == 2) {
|
|
1539
|
+
const keyType = convertJavaTypeArgument(arguments_[0], subject, location, filename, source, recordNames, typeParameters)
|
|
1540
|
+
|
|
1541
|
+
if (keyType.kind != "TypeReference") {
|
|
1542
|
+
return unsupportedSyntax("java", "map key type other than string", nodeLocation(arguments_[0], filename, source))
|
|
1543
|
+
}
|
|
1544
|
+
return mapType(
|
|
1545
|
+
keyType,
|
|
1546
|
+
convertJavaTypeArgument(arguments_[1], subject, location, filename, source, recordNames, typeParameters),
|
|
1547
|
+
nodeLocation(sourceType, filename, source),
|
|
1548
|
+
nodeLocation(arguments_[0], filename, source),
|
|
1549
|
+
nodeLocation(arguments_[1], filename, source)
|
|
1550
|
+
)
|
|
1551
|
+
}
|
|
1552
|
+
if (name == "java.util.SequencedMap" && arguments_.length == 2) {
|
|
1553
|
+
const keyType = convertJavaTypeArgument(arguments_[0], subject, location, filename, source, recordNames, typeParameters)
|
|
1554
|
+
|
|
1555
|
+
if (keyType.kind != "TypeReference" || keyType.name != "string") {
|
|
1556
|
+
return unsupportedSyntax("java", "map key type other than string", nodeLocation(arguments_[0], filename, source))
|
|
1557
|
+
}
|
|
1558
|
+
return mapType(
|
|
1559
|
+
keyType,
|
|
1560
|
+
convertJavaTypeArgument(arguments_[1], subject, location, filename, source, recordNames, typeParameters),
|
|
1561
|
+
nodeLocation(sourceType, filename, source),
|
|
1562
|
+
nodeLocation(arguments_[0], filename, source),
|
|
1563
|
+
nodeLocation(arguments_[1], filename, source)
|
|
1564
|
+
)
|
|
1565
|
+
}
|
|
1566
|
+
if ((name == "java.util.Optional" || name == "Optional") && arguments_.length == 1) {
|
|
1567
|
+
return optionalType(
|
|
1568
|
+
convertJavaTypeArgument(arguments_[0], subject, location, filename, source, recordNames, typeParameters),
|
|
1569
|
+
nodeLocation(sourceType, filename, source),
|
|
1570
|
+
nodeLocation(arguments_[0], filename, source)
|
|
1571
|
+
)
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
const record = recordNames.get(name)
|
|
1575
|
+
|
|
1576
|
+
if (record?.id) {
|
|
1577
|
+
if (record.kind == "ClassDeclaration") {
|
|
1578
|
+
return unsupportedSyntax("java", "generic reference class type", nodeLocation(sourceType, filename, source))
|
|
1579
|
+
}
|
|
1580
|
+
return recordType(record.id, nodeLocation(nameNode, filename, source), arguments_.map((argument) =>
|
|
1581
|
+
convertJavaTypeArgument(argument, subject, location, filename, source, recordNames, typeParameters)))
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
return unsupportedSyntax("java", "unsupported generic type", nodeLocation(sourceType, filename, source))
|
|
1585
|
+
}
|
|
552
1586
|
if (!["PrimitiveType", "TypeName", "ScopedTypeName"].includes(sourceType.name)) {
|
|
553
1587
|
return unsupportedSyntax("java", "unsupported scalar type", location)
|
|
554
1588
|
}
|
|
555
1589
|
|
|
1590
|
+
const typeParameter = typeParameters.get(nodeText(sourceType, source))
|
|
1591
|
+
|
|
1592
|
+
if (typeParameter?.id) return typeVariable(typeParameter.id, nodeLocation(sourceType, filename, source))
|
|
1593
|
+
const declaration = recordNames.get(nodeText(sourceType, source))
|
|
1594
|
+
|
|
1595
|
+
if (declaration?.id) return declaration.kind == "ClassDeclaration"
|
|
1596
|
+
? referenceType(declaration.id, nodeLocation(sourceType, filename, source))
|
|
1597
|
+
: declaration.kind == "EffectResourceDeclaration"
|
|
1598
|
+
? ownedResourceType(declaration.id, nodeLocation(sourceType, filename, source))
|
|
1599
|
+
: recordType(declaration.id, nodeLocation(sourceType, filename, source))
|
|
1600
|
+
|
|
556
1601
|
const type = sourceScalarType("java", nodeText(sourceType, source), nodeLocation(sourceType, filename, source))
|
|
557
1602
|
|
|
558
1603
|
if (!type) return unsupportedSyntax("java", "unsupported scalar type", nodeLocation(sourceType, location.filename, source))
|
|
@@ -560,29 +1605,142 @@ function convertType(sourceType, subject, location, filename, source) {
|
|
|
560
1605
|
return type
|
|
561
1606
|
}
|
|
562
1607
|
|
|
1608
|
+
/**
|
|
1609
|
+
* Converts one exact boxed Java collection type argument or nested collection.
|
|
1610
|
+
* @param {import("@lezer/common").SyntaxNode} node - Type argument node.
|
|
1611
|
+
* @param {string} subject - Typed subject.
|
|
1612
|
+
* @param {import("../semantic/types.js").SourceLocation} location - Owning location.
|
|
1613
|
+
* @param {string} filename - Source filename.
|
|
1614
|
+
* @param {string} source - Complete source.
|
|
1615
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} [recordNames] - Nominal declarations by source name.
|
|
1616
|
+
* @param {Map<string, import("../semantic/types.js").TypeParameter>} [typeParameters] - Declaration-scoped parameters.
|
|
1617
|
+
* @returns {import("../semantic/types.js").SemanticValueType} Semantic type.
|
|
1618
|
+
*/
|
|
1619
|
+
function convertJavaTypeArgument(node, subject, location, filename, source, recordNames = new Map(), typeParameters = new Map()) {
|
|
1620
|
+
if (node.name == "GenericType") return convertType(node, subject, location, filename, source, recordNames, typeParameters)
|
|
1621
|
+
const typeParameter = typeParameters.get(nodeText(node, source))
|
|
1622
|
+
|
|
1623
|
+
if (typeParameter?.id) return typeVariable(typeParameter.id, nodeLocation(node, filename, source))
|
|
1624
|
+
const declaration = recordNames.get(nodeText(node, source))
|
|
1625
|
+
|
|
1626
|
+
if (declaration?.id) return declaration.kind == "ClassDeclaration"
|
|
1627
|
+
? referenceType(declaration.id, nodeLocation(node, filename, source))
|
|
1628
|
+
: declaration.kind == "EffectResourceDeclaration"
|
|
1629
|
+
? ownedResourceType(declaration.id, nodeLocation(node, filename, source))
|
|
1630
|
+
: recordType(declaration.id, nodeLocation(node, filename, source))
|
|
1631
|
+
const spelling = nodeText(node, source)
|
|
1632
|
+
const scalar = spelling == "Integer" ? "integer" : spelling == "Boolean" ? "boolean" : spelling == "String" ? "string" : undefined
|
|
1633
|
+
|
|
1634
|
+
if (!scalar) return unsupportedSyntax("java", "unsupported collection type argument", nodeLocation(node, filename, source))
|
|
1635
|
+
const type = {kind: /** @type {const} */ ("TypeReference"), name: /** @type {import("../semantic/types.js").SemanticTypeName} */ (scalar)}
|
|
1636
|
+
|
|
1637
|
+
return withParserRanges(type, {type: nodeLocation(node, filename, source)})
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
/**
|
|
1641
|
+
* Converts one explicit Java function return type.
|
|
1642
|
+
* @param {import("@lezer/common").SyntaxNode | null} sourceType - Java return type node.
|
|
1643
|
+
* @param {string} subject - Typed function return.
|
|
1644
|
+
* @param {import("../semantic/types.js").SourceLocation} location - Function location.
|
|
1645
|
+
* @param {string} filename - Source filename.
|
|
1646
|
+
* @param {string} source - Complete source.
|
|
1647
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} [recordNames] - Nominal declarations by source name.
|
|
1648
|
+
* @param {Map<string, import("../semantic/types.js").TypeParameter>} [typeParameters] - Declaration-scoped parameters.
|
|
1649
|
+
* @returns {import("../semantic/types.js").SemanticFunctionReturnType} Semantic return type.
|
|
1650
|
+
*/
|
|
1651
|
+
function convertReturnType(sourceType, subject, location, filename, source, recordNames = new Map(), typeParameters = new Map()) {
|
|
1652
|
+
if (sourceType?.name == "void") {
|
|
1653
|
+
return requireSourceReturnType("java", "void", subject, location, nodeLocation(sourceType, filename, source))
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
return convertType(sourceType, subject, location, filename, source, recordNames, typeParameters)
|
|
1657
|
+
}
|
|
1658
|
+
|
|
563
1659
|
/**
|
|
564
1660
|
* Returns the direct Java declaration type node supported by scalar conversion.
|
|
565
1661
|
* @param {import("@lezer/common").SyntaxNode} node - Declaration node.
|
|
566
1662
|
* @returns {import("@lezer/common").SyntaxNode | null} Type syntax node.
|
|
567
1663
|
*/
|
|
568
1664
|
function declarationType(node) {
|
|
569
|
-
return node.getChild("PrimitiveType") ?? node.getChild("TypeName") ?? node.getChild("ScopedTypeName")
|
|
1665
|
+
return node.getChild("GenericType") ?? node.getChild("PrimitiveType") ?? node.getChild("TypeName") ?? node.getChild("ScopedTypeName") ?? node.getChild("void")
|
|
570
1666
|
}
|
|
571
1667
|
|
|
572
1668
|
/**
|
|
573
|
-
* Converts
|
|
1669
|
+
* Converts one native unbounded invariant Java type-parameter list.
|
|
1670
|
+
* @param {import("@lezer/common").SyntaxNode | null} node - TypeParameters node.
|
|
1671
|
+
* @param {string} ownerId - Stable owning declaration identity.
|
|
1672
|
+
* @param {string} filename - Source filename.
|
|
1673
|
+
* @param {string} source - Complete source.
|
|
1674
|
+
* @returns {import("../semantic/types.js").TypeParameter[] | undefined} Semantic parameters.
|
|
1675
|
+
*/
|
|
1676
|
+
function convertJavaTypeParameters(node, ownerId, filename, source) {
|
|
1677
|
+
if (!node) return undefined
|
|
1678
|
+
const parameters = structuralChildren(node)
|
|
1679
|
+
|
|
1680
|
+
return parameters.map((parameter, index) => {
|
|
1681
|
+
const children = structuralChildren(parameter)
|
|
1682
|
+
const definition = children[0]
|
|
1683
|
+
|
|
1684
|
+
if (parameter.name != "TypeParameter" || children.length != 1 || definition?.name != "Definition") {
|
|
1685
|
+
return unsupportedSyntax("java", "bounded, annotated, or malformed type parameter", nodeLocation(parameter, filename, source))
|
|
1686
|
+
}
|
|
1687
|
+
const location = nodeLocation(definition, filename, source)
|
|
1688
|
+
|
|
1689
|
+
return withParserRanges({
|
|
1690
|
+
id: `${ownerId}:type:${index}`,
|
|
1691
|
+
kind: /** @type {const} */ ("TypeParameter"),
|
|
1692
|
+
location,
|
|
1693
|
+
name: nodeText(definition, source)
|
|
1694
|
+
}, {name: location})
|
|
1695
|
+
})
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
/**
|
|
1699
|
+
* Converts one supported Java function signature before adapting any body.
|
|
574
1700
|
* @param {import("@lezer/common").SyntaxNode} node - Method declaration.
|
|
575
1701
|
* @param {string} filename - Source filename.
|
|
576
1702
|
* @param {string} source - Complete source.
|
|
577
|
-
* @
|
|
1703
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} recordNames - Nominal declarations by source name.
|
|
1704
|
+
* @param {boolean} [programFunction] - Whether the canonical project profile admits explicit public or private visibility.
|
|
1705
|
+
* @param {string} [ownerId] - Stable function identity.
|
|
1706
|
+
* @param {boolean} [referenceModule] - Whether package-visible static functions are required by sibling reference classes.
|
|
1707
|
+
* @returns {JavaFunctionSignature} Semantic function signature.
|
|
578
1708
|
*/
|
|
579
|
-
function
|
|
1709
|
+
function convertFunctionSignature(node, filename, source, recordNames, programFunction = false, ownerId = "function:0",
|
|
1710
|
+
referenceModule = false) {
|
|
580
1711
|
const location = nodeLocation(node, filename, source)
|
|
1712
|
+
const modifiers = node.getChild("Modifiers")
|
|
1713
|
+
const modifierNames = modifiers ? structuralChildren(modifiers).map((modifier) => modifier.name) : []
|
|
1714
|
+
|
|
1715
|
+
const requiredModifiers = programFunction ? "public or private static" : referenceModule ? "private or package-visible static" : "private static"
|
|
1716
|
+
const modifiersText = modifierNames.join(" ")
|
|
1717
|
+
|
|
1718
|
+
if (programFunction ? !["public static", "private static"].includes(modifiersText) :
|
|
1719
|
+
referenceModule ? !["private static", "static"].includes(modifiersText) : modifiersText != "private static") {
|
|
1720
|
+
return unsupportedSyntax("java", `semantic function without ${requiredModifiers}`, modifiers ? nodeLocation(modifiers, filename, source) : location)
|
|
1721
|
+
}
|
|
1722
|
+
const typeParameters = node.getChild("TypeParameters")
|
|
1723
|
+
const throws = node.getChild("Throws")
|
|
1724
|
+
|
|
1725
|
+
if (throws) return unsupportedSyntax("java", "checked throws", nodeLocation(throws, filename, source))
|
|
1726
|
+
const semanticTypeParameters = convertJavaTypeParameters(typeParameters, ownerId, filename, source)
|
|
1727
|
+
const typeParameterNames = new Map((semanticTypeParameters ?? []).map((parameter) => [parameter.name, parameter]))
|
|
581
1728
|
const definition = requiredChild(node, "Definition", filename, source)
|
|
582
1729
|
const name = nodeText(definition, source)
|
|
583
1730
|
const parametersNode = requiredChild(node, "FormalParameters", filename, source)
|
|
584
|
-
const
|
|
1731
|
+
const parameterNodes = structuralChildren(parametersNode)
|
|
1732
|
+
const unsupportedParameter = parameterNodes.find((parameter) => parameter.name != "FormalParameter")
|
|
1733
|
+
|
|
1734
|
+
if (unsupportedParameter) {
|
|
1735
|
+
return unsupportedSyntax("java", "unsupported parameter form", nodeLocation(unsupportedParameter, filename, source))
|
|
1736
|
+
}
|
|
1737
|
+
const parameters = parameterNodes.map((parameter) => {
|
|
585
1738
|
const parameterLocation = nodeLocation(parameter, filename, source)
|
|
1739
|
+
const parameterChildren = structuralChildren(parameter)
|
|
1740
|
+
|
|
1741
|
+
if (parameterChildren.length != 2 || parameter.getChild("Modifiers") || parameter.getChild("Dimension")) {
|
|
1742
|
+
return unsupportedSyntax("java", "unsupported parameter form", parameterLocation)
|
|
1743
|
+
}
|
|
586
1744
|
const parameterNameNode = requiredChild(parameter, "Definition", filename, source)
|
|
587
1745
|
const parameterName = nodeText(parameterNameNode, source)
|
|
588
1746
|
|
|
@@ -590,22 +1748,371 @@ function convertFunction(node, filename, source) {
|
|
|
590
1748
|
kind: /** @type {const} */ ("Parameter"),
|
|
591
1749
|
location: parameterLocation,
|
|
592
1750
|
name: parameterName,
|
|
593
|
-
type: convertType(declarationType(parameter), `Parameter '${parameterName}'`, parameterLocation, filename, source
|
|
1751
|
+
type: convertType(declarationType(parameter), `Parameter '${parameterName}'`, parameterLocation, filename, source,
|
|
1752
|
+
recordNames, typeParameterNames)
|
|
594
1753
|
}
|
|
595
1754
|
|
|
596
1755
|
return withParserRanges(semanticParameter, {name: nodeLocation(parameterNameNode, filename, source)})
|
|
597
1756
|
})
|
|
1757
|
+
return {
|
|
1758
|
+
location,
|
|
1759
|
+
name,
|
|
1760
|
+
nameLocation: nodeLocation(definition, filename, source),
|
|
1761
|
+
parameters,
|
|
1762
|
+
returnType: convertReturnType(declarationType(node), `Function '${name}' return`, location, filename, source,
|
|
1763
|
+
recordNames, typeParameterNames),
|
|
1764
|
+
...(semanticTypeParameters ? {typeParameters: semanticTypeParameters} : {})
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
/**
|
|
1769
|
+
* Converts one Java function body using already-proved module signatures.
|
|
1770
|
+
* @param {import("@lezer/common").SyntaxNode} node - Method declaration.
|
|
1771
|
+
* @param {JavaFunctionSignature} signature - Preconverted function signature.
|
|
1772
|
+
* @param {Map<string, JavaFunctionSignature>} functions - Module function signatures.
|
|
1773
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} recordNames - Nominal declarations by source name.
|
|
1774
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration>} records - Record declarations by identity.
|
|
1775
|
+
* @param {Map<string, import("../semantic/types.js").ClassDeclaration>} classes - Reference classes by identity.
|
|
1776
|
+
* @param {Map<string, import("../semantic/types.js").ErrorDeclaration>} errorNames - Error declarations by source name.
|
|
1777
|
+
* @param {Map<string, import("../semantic/types.js").ErrorDeclaration>} errors - Error declarations by identity.
|
|
1778
|
+
* @param {string} filename - Source filename.
|
|
1779
|
+
* @param {string} source - Complete source.
|
|
1780
|
+
* @returns {import("../semantic/types.js").FunctionDeclaration} Semantic function.
|
|
1781
|
+
*/
|
|
1782
|
+
function convertFunction(node, signature, functions, recordNames, records, classes, errorNames, errors, filename, source) {
|
|
598
1783
|
const block = requiredChild(node, "Block", filename, source)
|
|
599
|
-
const
|
|
1784
|
+
const context = {
|
|
1785
|
+
bindings: new Map(signature.parameters.map((parameter) => [parameter.name, parameter.type])),
|
|
1786
|
+
classes,
|
|
1787
|
+
errorNames,
|
|
1788
|
+
erasedBindingNames: new Set(),
|
|
1789
|
+
errors,
|
|
1790
|
+
functions,
|
|
1791
|
+
recordNames,
|
|
1792
|
+
records,
|
|
1793
|
+
returnType: signature.returnType,
|
|
1794
|
+
typeParameters: new Map((signature.typeParameters ?? []).map((parameter) => [parameter.name, parameter]))
|
|
1795
|
+
}
|
|
600
1796
|
|
|
601
1797
|
return withParserRanges({
|
|
602
|
-
body,
|
|
1798
|
+
body: convertBlock(block, filename, source, context),
|
|
603
1799
|
kind: "FunctionDeclaration",
|
|
604
|
-
location,
|
|
605
|
-
name,
|
|
606
|
-
parameters,
|
|
607
|
-
returnType:
|
|
608
|
-
|
|
1800
|
+
location: signature.location,
|
|
1801
|
+
name: signature.name,
|
|
1802
|
+
parameters: signature.parameters,
|
|
1803
|
+
returnType: signature.returnType,
|
|
1804
|
+
...(signature.typeParameters ? {typeParameters: signature.typeParameters} : {})
|
|
1805
|
+
}, {name: signature.nameLocation})
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
/**
|
|
1809
|
+
* Converts one exact final RuntimeException subclass.
|
|
1810
|
+
* @param {import("@lezer/common").SyntaxNode} node - Java class declaration.
|
|
1811
|
+
* @param {import("../semantic/types.js").ErrorDeclaration} declaration - Predeclared semantic error.
|
|
1812
|
+
* @param {string} filename - Source filename.
|
|
1813
|
+
* @param {string} source - Complete source.
|
|
1814
|
+
* @param {boolean} [programError] - Whether public program visibility is admitted.
|
|
1815
|
+
* @returns {import("../semantic/types.js").ErrorDeclaration} Semantic error declaration.
|
|
1816
|
+
*/
|
|
1817
|
+
function convertJavaError(node, declaration, filename, source, programError = false) {
|
|
1818
|
+
const location = nodeLocation(node, filename, source)
|
|
1819
|
+
const modifiers = node.getChild("Modifiers")
|
|
1820
|
+
const modifierText = modifiers ? structuralChildren(modifiers).map((modifier) => modifier.name).join(" ") : ""
|
|
1821
|
+
const superclass = node.getChild("Superclass")
|
|
1822
|
+
const superclassChildren = superclass ? structuralChildren(superclass) : []
|
|
1823
|
+
const parent = superclassChildren.at(-1)
|
|
1824
|
+
const body = node.getChild("ClassBody")
|
|
1825
|
+
const members = body ? structuralChildren(body) : []
|
|
1826
|
+
const constructor = members[0]
|
|
1827
|
+
const parameters = constructor?.getChild("FormalParameters")
|
|
1828
|
+
const parameterNodes = parameters ? structuralChildren(parameters) : []
|
|
1829
|
+
const parameter = parameterNodes[0]
|
|
1830
|
+
const parameterType = parameter ? declarationType(parameter) : null
|
|
1831
|
+
const parameterName = parameter?.getChild("Definition")
|
|
1832
|
+
const constructorBody = constructor?.getChild("ConstructorBody")
|
|
1833
|
+
const constructorStatements = constructorBody ? structuralChildren(constructorBody) : []
|
|
1834
|
+
const superCall = constructorStatements[0]
|
|
1835
|
+
const superArguments = superCall?.getChild("ArgumentList")
|
|
1836
|
+
const superValues = superArguments ? structuralChildren(superArguments) : []
|
|
1837
|
+
|
|
1838
|
+
if ((programError ? !["public final", "final"].includes(modifierText) : modifierText != "final") ||
|
|
1839
|
+
!superclass || superclassChildren.length != 2 || parent?.name != "TypeName" || nodeText(parent, source) != "RuntimeException" ||
|
|
1840
|
+
node.getChild("SuperInterfaces") || node.getChild("TypeParameters") || !body || members.length != 1 ||
|
|
1841
|
+
constructor?.name != "ConstructorDeclaration" ||
|
|
1842
|
+
(programError ? nodeText(constructor.getChild("Modifiers") ?? constructor, source) != "public" : constructor.getChild("Modifiers")) ||
|
|
1843
|
+
constructor.getChild("Throws") ||
|
|
1844
|
+
nodeText(constructor.getChild("Definition") ?? node, source) != declaration.name || parameterNodes.length != 1 ||
|
|
1845
|
+
parameter?.name != "FormalParameter" || nodeText(parameterType ?? node, source) != "String" ||
|
|
1846
|
+
nodeText(parameterName ?? node, source) != "message" || !constructorBody || constructorStatements.length != 1 ||
|
|
1847
|
+
superCall?.name != "ExplicitConstructorInvocation" || !superCall.getChild("super") || superValues.length != 1 ||
|
|
1848
|
+
superValues[0].name != "Identifier" || nodeText(superValues[0], source) != "message") {
|
|
1849
|
+
return unsupportedSyntax("java", "noncanonical typed error declaration", location)
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
return withParserRanges(declaration, {
|
|
1853
|
+
name: nodeLocation(requiredChild(node, "Definition", filename, source), filename, source),
|
|
1854
|
+
type: nodeLocation(parent, filename, source)
|
|
1855
|
+
})
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
/**
|
|
1859
|
+
* Reads one package-private Java reference constructor or instance-method signature.
|
|
1860
|
+
* @param {import("@lezer/common").SyntaxNode} node - Constructor or method declaration.
|
|
1861
|
+
* @param {import("../semantic/types.js").ClassDeclaration} declaration - Declaring class.
|
|
1862
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} nominalNames - Visible nominal declarations.
|
|
1863
|
+
* @param {boolean} constructor - Whether this is the sole constructor.
|
|
1864
|
+
* @param {string} filename - Source filename.
|
|
1865
|
+
* @param {string} source - Complete source.
|
|
1866
|
+
* @returns {{location: import("../semantic/types.js").SourceLocation, name: string, nameLocation: import("../semantic/types.js").SourceLocation, parameters: import("../semantic/types.js").Parameter[], returnType?: import("../semantic/types.js").SemanticFunctionReturnType}} Exact signature.
|
|
1867
|
+
*/
|
|
1868
|
+
function convertJavaClassCallableSignature(node, declaration, nominalNames, constructor, filename, source) {
|
|
1869
|
+
const location = nodeLocation(node, filename, source)
|
|
1870
|
+
const definition = requiredChild(node, "Definition", filename, source)
|
|
1871
|
+
const name = nodeText(definition, source)
|
|
1872
|
+
const parametersNode = requiredChild(node, "FormalParameters", filename, source)
|
|
1873
|
+
const parameterNodes = structuralChildren(parametersNode)
|
|
1874
|
+
const modifiers = node.getChild("Modifiers")
|
|
1875
|
+
|
|
1876
|
+
if (modifiers || node.getChild("Throws") || node.getChild("TypeParameters") ||
|
|
1877
|
+
constructor != (node.name == "ConstructorDeclaration") || constructor && name != declaration.name) {
|
|
1878
|
+
return unsupportedSyntax("java", "noncanonical reference class callable", location)
|
|
1879
|
+
}
|
|
1880
|
+
if (!constructor && referenceMethodHooks.has(name)) {
|
|
1881
|
+
return unsupportedSyntax("java", `reserved reference method '${name}'`, nodeLocation(definition, filename, source))
|
|
1882
|
+
}
|
|
1883
|
+
const parameters = parameterNodes.map((parameter) => {
|
|
1884
|
+
const parameterLocation = nodeLocation(parameter, filename, source)
|
|
1885
|
+
const children = structuralChildren(parameter)
|
|
1886
|
+
const parameterNameNode = parameter.getChild("Definition")
|
|
1887
|
+
|
|
1888
|
+
if (parameter.name != "FormalParameter" || children.length != 2 || parameter.getChild("Modifiers") ||
|
|
1889
|
+
parameter.getChild("Dimension") || !parameterNameNode) {
|
|
1890
|
+
return unsupportedSyntax("java", "unsupported reference class parameter", parameterLocation)
|
|
1891
|
+
}
|
|
1892
|
+
const parameterName = nodeText(parameterNameNode, source)
|
|
1893
|
+
|
|
1894
|
+
return withParserRanges({kind: /** @type {const} */ ("Parameter"), location: parameterLocation, name: parameterName,
|
|
1895
|
+
type: convertType(declarationType(parameter), `Parameter '${parameterName}'`, parameterLocation, filename, source, nominalNames)},
|
|
1896
|
+
{name: nodeLocation(parameterNameNode, filename, source)})
|
|
1897
|
+
})
|
|
1898
|
+
|
|
1899
|
+
return {location, name: constructor ? "constructor" : name, nameLocation: nodeLocation(definition, filename, source), parameters,
|
|
1900
|
+
...(constructor ? {} : {returnType: convertReturnType(declarationType(node), `Method '${name}' return`, location,
|
|
1901
|
+
filename, source, nominalNames)})}
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
/**
|
|
1905
|
+
* Converts the bounded final/private-field Java reference-class profile.
|
|
1906
|
+
* @param {import("@lezer/common").SyntaxNode} node - Class declaration.
|
|
1907
|
+
* @param {import("../semantic/types.js").ClassDeclaration} declaration - Predeclared class identity.
|
|
1908
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} nominalNames - Visible nominal declarations.
|
|
1909
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration>} records - Records by identity.
|
|
1910
|
+
* @param {Map<string, import("../semantic/types.js").ClassDeclaration>} classes - Classes by identity.
|
|
1911
|
+
* @param {Map<string, JavaFunctionSignature>} functions - Top-level signatures.
|
|
1912
|
+
* @param {Map<string, import("../semantic/types.js").ErrorDeclaration>} errorNames - Errors by name.
|
|
1913
|
+
* @param {Map<string, import("../semantic/types.js").ErrorDeclaration>} errors - Errors by identity.
|
|
1914
|
+
* @param {string} filename - Source filename.
|
|
1915
|
+
* @param {string} source - Complete source.
|
|
1916
|
+
* @returns {import("../semantic/types.js").ClassDeclaration} Semantic class.
|
|
1917
|
+
*/
|
|
1918
|
+
function convertJavaReferenceClass(node, declaration, nominalNames, records, classes, functions, errorNames, errors,
|
|
1919
|
+
filename, source) {
|
|
1920
|
+
const location = nodeLocation(node, filename, source)
|
|
1921
|
+
const modifiers = node.getChild("Modifiers")
|
|
1922
|
+
const modifierNames = modifiers ? structuralChildren(modifiers).map((modifier) => modifier.name) : []
|
|
1923
|
+
const body = requiredChild(node, "ClassBody", filename, source)
|
|
1924
|
+
const members = structuralChildren(body)
|
|
1925
|
+
const fieldNodes = members.filter((member) => member.name == "FieldDeclaration")
|
|
1926
|
+
const constructorNodes = members.filter((member) => member.name == "ConstructorDeclaration")
|
|
1927
|
+
const methodNodes = members.filter((member) => member.name == "MethodDeclaration")
|
|
1928
|
+
|
|
1929
|
+
if (modifierNames.join(" ") != "final" || node.getChild("Superclass") || node.getChild("SuperInterfaces") ||
|
|
1930
|
+
node.getChild("TypeParameters") || constructorNodes.length != 1 || fieldNodes.length == 0 ||
|
|
1931
|
+
fieldNodes.length + constructorNodes.length + methodNodes.length != members.length ||
|
|
1932
|
+
members.slice(0, fieldNodes.length).some((member) => member.name != "FieldDeclaration") ||
|
|
1933
|
+
members[fieldNodes.length] != constructorNodes[0] ||
|
|
1934
|
+
members.slice(fieldNodes.length + 1).some((member) => member.name != "MethodDeclaration")) {
|
|
1935
|
+
return unsupportedSyntax("java", "noncanonical reference class declaration", location)
|
|
1936
|
+
}
|
|
1937
|
+
declaration.fields = fieldNodes.map((fieldNode, index) => {
|
|
1938
|
+
const fieldLocation = nodeLocation(fieldNode, filename, source)
|
|
1939
|
+
const fieldModifiers = fieldNode.getChild("Modifiers")
|
|
1940
|
+
const names = fieldModifiers ? structuralChildren(fieldModifiers).map((modifier) => modifier.name) : []
|
|
1941
|
+
const variableNodes = fieldNode.getChildren("VariableDeclarator")
|
|
1942
|
+
const variable = variableNodes[0]
|
|
1943
|
+
const definition = variable ? variable.getChild("Definition") : null
|
|
1944
|
+
|
|
1945
|
+
if (names.join(" ") != "private" || variableNodes.length != 1 || !definition ||
|
|
1946
|
+
structuralChildren(variable).length != 1 || fieldNode.getChild("Dimension")) {
|
|
1947
|
+
return unsupportedSyntax("java", "noncanonical private instance field", fieldLocation)
|
|
1948
|
+
}
|
|
1949
|
+
const name = nodeText(definition, source)
|
|
1950
|
+
|
|
1951
|
+
return withParserRanges({id: `${declaration.id}:field:${index}`, kind: /** @type {const} */ ("PrivateField"),
|
|
1952
|
+
location: fieldLocation, name,
|
|
1953
|
+
type: convertType(declarationType(fieldNode), `Private field '${name}'`, fieldLocation, filename, source, nominalNames)},
|
|
1954
|
+
{name: nodeLocation(definition, filename, source)})
|
|
1955
|
+
})
|
|
1956
|
+
const constructorNode = constructorNodes[0]
|
|
1957
|
+
const constructorSignature = convertJavaClassCallableSignature(constructorNode, declaration, nominalNames, true, filename, source)
|
|
1958
|
+
|
|
1959
|
+
declaration.constructor = withParserRanges({body: /** @type {import("../semantic/types.js").Block} */ ({}),
|
|
1960
|
+
id: `${declaration.id}:constructor`, kind: /** @type {const} */ ("ConstructorDeclaration"),
|
|
1961
|
+
location: constructorSignature.location, parameters: constructorSignature.parameters}, {constructor: constructorSignature.nameLocation})
|
|
1962
|
+
const methodSignatures = methodNodes.map((method) =>
|
|
1963
|
+
convertJavaClassCallableSignature(method, declaration, nominalNames, false, filename, source))
|
|
1964
|
+
|
|
1965
|
+
declaration.methods = methodSignatures.map((signature, index) => withParserRanges({
|
|
1966
|
+
body: /** @type {import("../semantic/types.js").Block} */ ({}), id: `${declaration.id}:method:${index}`,
|
|
1967
|
+
kind: /** @type {const} */ ("MethodDeclaration"), location: signature.location, name: signature.name,
|
|
1968
|
+
parameters: signature.parameters,
|
|
1969
|
+
returnType: /** @type {import("../semantic/types.js").SemanticFunctionReturnType} */ (signature.returnType)
|
|
1970
|
+
}, {name: signature.nameLocation}))
|
|
1971
|
+
const base = {bindings: new Map(), classes, currentClass: declaration, errorNames, erasedBindingNames: new Set(), errors,
|
|
1972
|
+
functions, recordNames: nominalNames, records}
|
|
1973
|
+
const constructorBody = requiredChild(constructorNode, "ConstructorBody", filename, source)
|
|
1974
|
+
|
|
1975
|
+
declaration.constructor.body = convertBlock(constructorBody, filename, source,
|
|
1976
|
+
{...base, bindings: new Map(declaration.constructor.parameters.map((parameter) => [parameter.name, parameter.type])),
|
|
1977
|
+
returnType: {kind: /** @type {const} */ ("TypeReference"), name: /** @type {const} */ ("void")}})
|
|
1978
|
+
for (let index = 0; index < methodNodes.length; index += 1) {
|
|
1979
|
+
const method = declaration.methods[index]
|
|
1980
|
+
|
|
1981
|
+
method.body = convertBlock(requiredChild(methodNodes[index], "Block", filename, source), filename, source,
|
|
1982
|
+
{...base, bindings: new Map(method.parameters.map((parameter) => [parameter.name, parameter.type])), returnType: method.returnType})
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
return withParserRanges(declaration, {name: nodeLocation(requiredChild(node, "Definition", filename, source), filename, source)})
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
/**
|
|
1989
|
+
* Converts one conventional final Java class with exact private-final storage,
|
|
1990
|
+
* constructor initialization, and canonical field-name accessors.
|
|
1991
|
+
* @param {import("@lezer/common").SyntaxNode} node - Class declaration.
|
|
1992
|
+
* @param {import("../semantic/types.js").RecordDeclaration} declaration - Predeclared nominal identity.
|
|
1993
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration>} recordNames - Records by source name.
|
|
1994
|
+
* @param {string} filename - Source filename.
|
|
1995
|
+
* @param {string} source - Complete source.
|
|
1996
|
+
* @param {boolean} [programRecord] - Whether the canonical project profile admits public or package-private class visibility.
|
|
1997
|
+
* @returns {import("../semantic/types.js").RecordDeclaration} Semantic record.
|
|
1998
|
+
*/
|
|
1999
|
+
function convertJavaRecord(node, declaration, recordNames, filename, source, programRecord = false) {
|
|
2000
|
+
const location = nodeLocation(node, filename, source)
|
|
2001
|
+
const typeParameterNames = new Map((declaration.typeParameters ?? []).map((parameter) => [parameter.name, parameter]))
|
|
2002
|
+
const modifiers = node.getChild("Modifiers")
|
|
2003
|
+
const modifierNames = modifiers ? structuralChildren(modifiers).map((modifier) => modifier.name) : []
|
|
2004
|
+
|
|
2005
|
+
const modifiersText = modifierNames.join(" ")
|
|
2006
|
+
|
|
2007
|
+
if ((programRecord ? !["public final", "final"].includes(modifiersText) : modifiersText != "final") ||
|
|
2008
|
+
node.getChild("Superclass") || node.getChild("SuperInterfaces")) {
|
|
2009
|
+
return unsupportedSyntax("java", "noncanonical record class modifiers", location)
|
|
2010
|
+
}
|
|
2011
|
+
const classBody = requiredChild(node, "ClassBody", filename, source)
|
|
2012
|
+
const members = structuralChildren(classBody)
|
|
2013
|
+
const fields = members.filter((member) => member.name == "FieldDeclaration")
|
|
2014
|
+
const constructors = members.filter((member) => member.name == "ConstructorDeclaration")
|
|
2015
|
+
const accessors = members.filter((member) => member.name == "MethodDeclaration")
|
|
2016
|
+
const unsupported = members.find((member) => !["FieldDeclaration", "ConstructorDeclaration", "MethodDeclaration"].includes(member.name))
|
|
2017
|
+
|
|
2018
|
+
if (unsupported || constructors.length != 1 || accessors.length != fields.length ||
|
|
2019
|
+
members.slice(0, fields.length).some((member) => member.name != "FieldDeclaration") || members[fields.length] != constructors[0] ||
|
|
2020
|
+
members.slice(fields.length + 1).some((member) => member.name != "MethodDeclaration")) {
|
|
2021
|
+
return unsupportedSyntax("java", "record class member order or shape", nodeLocation(unsupported ?? node, filename, source))
|
|
2022
|
+
}
|
|
2023
|
+
declaration.fields = fields.map((fieldNode) => {
|
|
2024
|
+
const fieldLocation = nodeLocation(fieldNode, filename, source)
|
|
2025
|
+
const fieldModifiers = fieldNode.getChild("Modifiers")
|
|
2026
|
+
const names = fieldModifiers ? structuralChildren(fieldModifiers).map((modifier) => modifier.name) : []
|
|
2027
|
+
const variable = requiredChild(fieldNode, "VariableDeclarator", filename, source)
|
|
2028
|
+
const definition = requiredChild(variable, "Definition", filename, source)
|
|
2029
|
+
const variableChildren = structuralChildren(variable)
|
|
2030
|
+
|
|
2031
|
+
if (names.join(" ") != "private final" || variableChildren.length != 1 || fieldNode.getChild("Dimension")) {
|
|
2032
|
+
return unsupportedSyntax("java", "record field outside private final storage profile", fieldLocation)
|
|
2033
|
+
}
|
|
2034
|
+
const field = {
|
|
2035
|
+
kind: /** @type {const} */ ("RecordField"),
|
|
2036
|
+
location: fieldLocation,
|
|
2037
|
+
name: nodeText(definition, source),
|
|
2038
|
+
type: convertType(declarationType(fieldNode), `Record field '${nodeText(definition, source)}'`, fieldLocation, filename, source,
|
|
2039
|
+
recordNames, typeParameterNames)
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
return withParserRanges(field, {name: nodeLocation(definition, filename, source)})
|
|
2043
|
+
})
|
|
2044
|
+
const constructor = constructors[0]
|
|
2045
|
+
const constructorName = requiredChild(constructor, "Definition", filename, source)
|
|
2046
|
+
const parametersNode = requiredChild(constructor, "FormalParameters", filename, source)
|
|
2047
|
+
const parameters = structuralChildren(parametersNode)
|
|
2048
|
+
const body = requiredChild(constructor, "ConstructorBody", filename, source)
|
|
2049
|
+
const assignments = structuralChildren(body)
|
|
2050
|
+
|
|
2051
|
+
const constructorModifiers = constructor.getChild("Modifiers")
|
|
2052
|
+
const constructorModifierNames = constructorModifiers ? structuralChildren(constructorModifiers).map((modifier) => modifier.name) : []
|
|
2053
|
+
|
|
2054
|
+
if (nodeText(constructorName, source) != declaration.name ||
|
|
2055
|
+
constructorModifierNames.join(" ") != (programRecord ? "public" : "") || constructor.getChild("Throws") ||
|
|
2056
|
+
constructor.getChild("TypeParameters") || parameters.length != declaration.fields.length || assignments.length != declaration.fields.length) {
|
|
2057
|
+
return unsupportedSyntax("java", "noncanonical record constructor", nodeLocation(constructor, filename, source))
|
|
2058
|
+
}
|
|
2059
|
+
for (let index = 0; index < declaration.fields.length; index += 1) {
|
|
2060
|
+
const field = declaration.fields[index]
|
|
2061
|
+
const parameter = parameters[index]
|
|
2062
|
+
const parameterName = parameter?.getChild("Definition")
|
|
2063
|
+
const assignment = assignments[index]
|
|
2064
|
+
const assignmentExpression = assignment?.getChild("AssignmentExpression")
|
|
2065
|
+
const assignmentChildren = assignmentExpression ? structuralChildren(assignmentExpression) : []
|
|
2066
|
+
const fieldAccess = assignmentChildren[0]
|
|
2067
|
+
const operator = assignmentChildren[1]
|
|
2068
|
+
const value = assignmentChildren[2]
|
|
2069
|
+
const parameterType = parameter ? convertType(declarationType(parameter), `Record constructor parameter '${field.name}'`,
|
|
2070
|
+
nodeLocation(parameter, filename, source), filename, source, recordNames, typeParameterNames) : undefined
|
|
2071
|
+
|
|
2072
|
+
if (parameter?.name != "FormalParameter" || !parameterName || nodeText(parameterName, source) != field.name ||
|
|
2073
|
+
parameter.getChild("Modifiers") || parameter.getChild("Dimension") || !parameterType || !sameValueType(parameterType, field.type) ||
|
|
2074
|
+
assignment?.name != "ExpressionStatement" || assignmentChildren.length != 3 || fieldAccess?.name != "FieldAccess" ||
|
|
2075
|
+
nodeText(fieldAccess, source) != `this.${field.name}` || operator?.name != "AssignOp" || nodeText(operator, source) != "=" ||
|
|
2076
|
+
value?.name != "Identifier" || nodeText(value, source) != field.name) {
|
|
2077
|
+
return unsupportedSyntax("java", "record field not initialized exactly once", nodeLocation(assignment ?? constructor, filename, source))
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
for (let index = 0; index < declaration.fields.length; index += 1) {
|
|
2081
|
+
const field = declaration.fields[index]
|
|
2082
|
+
const accessor = accessors[index]
|
|
2083
|
+
const name = requiredChild(accessor, "Definition", filename, source)
|
|
2084
|
+
const parameters = requiredChild(accessor, "FormalParameters", filename, source)
|
|
2085
|
+
const block = requiredChild(accessor, "Block", filename, source)
|
|
2086
|
+
const statements = structuralChildren(block)
|
|
2087
|
+
const returned = statements[0]?.getChild("FieldAccess")
|
|
2088
|
+
const accessorType = convertType(declarationType(accessor), `Record accessor '${field.name}'`,
|
|
2089
|
+
nodeLocation(accessor, filename, source), filename, source, recordNames, typeParameterNames)
|
|
2090
|
+
|
|
2091
|
+
const accessorModifiers = accessor.getChild("Modifiers")
|
|
2092
|
+
const accessorModifierNames = accessorModifiers ? structuralChildren(accessorModifiers).map((modifier) => modifier.name) : []
|
|
2093
|
+
|
|
2094
|
+
if (accessorModifierNames.join(" ") != (programRecord ? "public" : "") || accessor.getChild("Throws") || accessor.getChild("TypeParameters") ||
|
|
2095
|
+
nodeText(name, source) != field.name || structuralChildren(parameters).length != 0 || statements.length != 1 ||
|
|
2096
|
+
statements[0].name != "ReturnStatement" || !returned || nodeText(returned, source) != `this.${field.name}` ||
|
|
2097
|
+
!sameValueType(accessorType, field.type)) {
|
|
2098
|
+
return unsupportedSyntax("java", "noncanonical record accessor", nodeLocation(accessor, filename, source))
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
return withParserRanges(declaration, {name: nodeLocation(requiredChild(node, "Definition", filename, source), filename, source)})
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
/**
|
|
2106
|
+
* Detects only the pinned grammar's parser-backed recovery shape for Java record syntax.
|
|
2107
|
+
* @param {import("@lezer/common").SyntaxNode} root - Program root.
|
|
2108
|
+
* @param {string} source - Complete source.
|
|
2109
|
+
* @returns {import("@lezer/common").SyntaxNode | undefined} The parser-owned `record` type token.
|
|
2110
|
+
*/
|
|
2111
|
+
function javaRecordRecoveryToken(root, source) {
|
|
2112
|
+
const first = structuralChildren(root)[0]
|
|
2113
|
+
const type = first?.name == "LocalVariableDeclaration" ? first.getChild("TypeName") : null
|
|
2114
|
+
|
|
2115
|
+
return type && nodeText(type, source) == "record" ? type : undefined
|
|
609
2116
|
}
|
|
610
2117
|
|
|
611
2118
|
/**
|
|
@@ -613,9 +2120,10 @@ function convertFunction(node, filename, source) {
|
|
|
613
2120
|
* @param {import("@lezer/common").SyntaxNode} node - Java if statement.
|
|
614
2121
|
* @param {string} filename - Source filename.
|
|
615
2122
|
* @param {string} source - Complete source.
|
|
2123
|
+
* @param {JavaConversionContext} context - Typed lexical conversion context.
|
|
616
2124
|
* @returns {import("../semantic/types.js").IfStatement} Semantic branch.
|
|
617
2125
|
*/
|
|
618
|
-
function convertIf(node, filename, source) {
|
|
2126
|
+
function convertIf(node, filename, source, context) {
|
|
619
2127
|
const location = nodeLocation(node, filename, source)
|
|
620
2128
|
const conditionContainer = requiredChild(node, "ParenthesizedExpression", filename, source)
|
|
621
2129
|
const conditionNodes = structuralChildren(conditionContainer)
|
|
@@ -630,14 +2138,18 @@ function convertIf(node, filename, source) {
|
|
|
630
2138
|
return unsupportedSyntax("java", "if without block consequent", location)
|
|
631
2139
|
}
|
|
632
2140
|
|
|
2141
|
+
const semanticCondition = convertExpression(condition, filename, source, context)
|
|
633
2142
|
let alternate
|
|
2143
|
+
const consequentContext = {...context, bindings: new Map(context.bindings), erasedBindingNames: new Set(context.erasedBindingNames)}
|
|
2144
|
+
const alternateContext = {...context, bindings: new Map(context.bindings), erasedBindingNames: new Set(context.erasedBindingNames)}
|
|
634
2145
|
|
|
635
|
-
if (alternateNode?.name == "Block")
|
|
636
|
-
|
|
2146
|
+
if (alternateNode?.name == "Block") {
|
|
2147
|
+
alternate = convertBlock(alternateNode, filename, source, alternateContext)
|
|
2148
|
+
} else if (alternateNode?.name == "IfStatement") {
|
|
637
2149
|
alternate = {
|
|
638
2150
|
kind: /** @type {const} */ ("Block"),
|
|
639
2151
|
location: nodeLocation(alternateNode, filename, source),
|
|
640
|
-
statements: [convertIf(alternateNode, filename, source)]
|
|
2152
|
+
statements: [convertIf(alternateNode, filename, source, alternateContext)]
|
|
641
2153
|
}
|
|
642
2154
|
} else if (alternateNode && alternateNode.name != "else") {
|
|
643
2155
|
return unsupportedSyntax("java", `if alternate ${alternateNode.name}`, nodeLocation(alternateNode, filename, source))
|
|
@@ -645,24 +2157,89 @@ function convertIf(node, filename, source) {
|
|
|
645
2157
|
|
|
646
2158
|
return {
|
|
647
2159
|
...(alternate ? {alternate} : {}),
|
|
648
|
-
condition:
|
|
649
|
-
consequent: convertBlock(consequentNode, filename, source),
|
|
2160
|
+
condition: semanticCondition,
|
|
2161
|
+
consequent: convertBlock(consequentNode, filename, source, consequentContext),
|
|
650
2162
|
kind: "IfStatement",
|
|
651
2163
|
location
|
|
652
2164
|
}
|
|
653
2165
|
}
|
|
654
2166
|
|
|
2167
|
+
/**
|
|
2168
|
+
* Converts one exact block-bodied Java pre-condition loop.
|
|
2169
|
+
* @param {import("@lezer/common").SyntaxNode} node - Java WhileStatement.
|
|
2170
|
+
* @param {string} filename - Source filename.
|
|
2171
|
+
* @param {string} source - Complete source.
|
|
2172
|
+
* @param {JavaConversionContext} context - Typed lexical conversion context.
|
|
2173
|
+
* @returns {import("../semantic/types.js").WhileStatement} Semantic loop.
|
|
2174
|
+
*/
|
|
2175
|
+
function convertWhile(node, filename, source, context) {
|
|
2176
|
+
const location = nodeLocation(node, filename, source)
|
|
2177
|
+
const direct = directChildren(node)
|
|
2178
|
+
const conditionContainer = node.getChild("ParenthesizedExpression")
|
|
2179
|
+
const body = node.getChild("Block")
|
|
2180
|
+
const conditionNodes = conditionContainer ? structuralChildren(conditionContainer) : []
|
|
2181
|
+
|
|
2182
|
+
if (direct.length != 3 || direct[0].name != "while" || direct[1].name != "ParenthesizedExpression" ||
|
|
2183
|
+
direct[2].name != "Block" || !conditionContainer || !body || conditionNodes.length != 1) {
|
|
2184
|
+
return unsupportedSyntax("java", "while without exact condition and block body", location)
|
|
2185
|
+
}
|
|
2186
|
+
const bodyContext = {
|
|
2187
|
+
...context,
|
|
2188
|
+
bindings: new Map(context.bindings),
|
|
2189
|
+
erasedBindingNames: new Set(context.erasedBindingNames)
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2192
|
+
return withParserRanges({
|
|
2193
|
+
body: convertBlock(body, filename, source, bodyContext),
|
|
2194
|
+
condition: convertExpression(conditionNodes[0], filename, source, context),
|
|
2195
|
+
kind: /** @type {const} */ ("WhileStatement"),
|
|
2196
|
+
location
|
|
2197
|
+
}, {keyword: nodeLocation(direct[0], filename, source)})
|
|
2198
|
+
}
|
|
2199
|
+
|
|
655
2200
|
/**
|
|
656
2201
|
* Converts Java's supported main method.
|
|
657
2202
|
* @param {import("@lezer/common").SyntaxNode} node - Main method.
|
|
658
2203
|
* @param {string} filename - Source filename.
|
|
659
2204
|
* @param {string} source - Complete source.
|
|
2205
|
+
* @param {Map<string, JavaFunctionSignature>} functions - Module function signatures.
|
|
2206
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} recordNames - Nominal declarations by source name.
|
|
2207
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration>} records - Record declarations by identity.
|
|
2208
|
+
* @param {Map<string, import("../semantic/types.js").ClassDeclaration>} classes - Reference classes by identity.
|
|
2209
|
+
* @param {Map<string, import("../semantic/types.js").ErrorDeclaration>} errorNames - Error declarations by source name.
|
|
2210
|
+
* @param {Map<string, import("../semantic/types.js").ErrorDeclaration>} errors - Error declarations by identity.
|
|
660
2211
|
* @returns {import("../semantic/types.js").EntryPoint} Semantic entry point.
|
|
661
2212
|
*/
|
|
662
|
-
function convertEntryPoint(node, filename, source) {
|
|
2213
|
+
function convertEntryPoint(node, filename, source, functions, recordNames, records, classes, errorNames, errors) {
|
|
663
2214
|
const location = nodeLocation(node, filename, source)
|
|
2215
|
+
const modifiers = node.getChild("Modifiers")
|
|
2216
|
+
const parameters = node.getChild("FormalParameters")
|
|
2217
|
+
const definition = node.getChild("Definition")
|
|
2218
|
+
const returnType = node.getChild("void")
|
|
2219
|
+
const modifierNames = modifiers ? structuralChildren(modifiers).map((modifier) => modifier.name) : []
|
|
2220
|
+
const parameterNodes = parameters ? structuralChildren(parameters) : []
|
|
2221
|
+
const parameter = parameterNodes[0]
|
|
2222
|
+
const parameterType = parameter?.getChild("ArrayType")
|
|
2223
|
+
const parameterName = parameter?.getChild("Definition")
|
|
2224
|
+
|
|
2225
|
+
if (modifierNames.join(" ") != "public static" || !parameters || parameterNodes.length != 1 ||
|
|
2226
|
+
parameter?.name != "FormalParameter" || nodeText(parameterType ?? node, source) != "String[]" ||
|
|
2227
|
+
nodeText(parameterName ?? node, source) != "args" || !definition || nodeText(definition, source) != "main" || !returnType ||
|
|
2228
|
+
node.getChild("TypeParameters") || node.getChild("Throws")) {
|
|
2229
|
+
return unsupportedSyntax("java", "main method signature", location)
|
|
2230
|
+
}
|
|
664
2231
|
const block = requiredChild(node, "Block", filename, source)
|
|
665
|
-
const body = convertBlock(block, filename, source
|
|
2232
|
+
const body = convertBlock(block, filename, source, {
|
|
2233
|
+
bindings: new Map(),
|
|
2234
|
+
classes,
|
|
2235
|
+
errorNames,
|
|
2236
|
+
erasedBindingNames: new Set(),
|
|
2237
|
+
errors,
|
|
2238
|
+
functions,
|
|
2239
|
+
recordNames,
|
|
2240
|
+
records,
|
|
2241
|
+
returnType: undefined
|
|
2242
|
+
})
|
|
666
2243
|
|
|
667
2244
|
return {body, kind: "EntryPoint", location}
|
|
668
2245
|
}
|
|
@@ -672,9 +2249,10 @@ function convertEntryPoint(node, filename, source) {
|
|
|
672
2249
|
* @param {import("@lezer/common").SyntaxNode} statement - Expression statement.
|
|
673
2250
|
* @param {string} filename - Source filename.
|
|
674
2251
|
* @param {string} source - Complete source.
|
|
2252
|
+
* @param {JavaConversionContext} context - Typed lexical conversion context.
|
|
675
2253
|
* @returns {import("../semantic/types.js").PrintStatement} Semantic print.
|
|
676
2254
|
*/
|
|
677
|
-
function convertPrint(statement, filename, source) {
|
|
2255
|
+
function convertPrint(statement, filename, source, context) {
|
|
678
2256
|
const location = nodeLocation(statement, filename, source)
|
|
679
2257
|
|
|
680
2258
|
const printInvocation = statement.getChild("MethodInvocation")
|
|
@@ -697,7 +2275,7 @@ function convertPrint(statement, filename, source) {
|
|
|
697
2275
|
|
|
698
2276
|
const printLocation = nodeLocation(printInvocation, filename, source)
|
|
699
2277
|
|
|
700
|
-
return {expression: convertExpression(arguments_[0], filename, source), kind: "PrintStatement", location: printLocation}
|
|
2278
|
+
return {expression: convertExpression(arguments_[0], filename, source, context), kind: "PrintStatement", location: printLocation}
|
|
701
2279
|
}
|
|
702
2280
|
|
|
703
2281
|
/**
|
|
@@ -705,13 +2283,18 @@ function convertPrint(statement, filename, source) {
|
|
|
705
2283
|
* @param {object} input - Parser input.
|
|
706
2284
|
* @param {string} input.filename - Source filename.
|
|
707
2285
|
* @param {string} input.source - Source text.
|
|
2286
|
+
* @param {readonly import("../semantic/types.js").EffectCapabilityDeclaration[]} [input.capabilities] - Compiler-authorized declarations.
|
|
2287
|
+
* @param {{isEntry: boolean, functions: Map<string, import("../semantic/types.js").FunctionDeclaration>, records: Map<string, import("../semantic/types.js").RecordDeclaration>, errors?: Map<string, import("../semantic/types.js").ErrorDeclaration>}} [input.program] - Resolved program imports and entry role.
|
|
708
2288
|
* @returns {import("../semantic/types.js").SemanticModule} Semantic module.
|
|
709
2289
|
*/
|
|
710
|
-
export function parseJava({filename, source}) {
|
|
2290
|
+
export function parseJava({capabilities = [], filename, source, program}) {
|
|
711
2291
|
const tree = parser.parse(source)
|
|
712
2292
|
const error = descendants(tree.topNode, "⚠")[0]
|
|
713
2293
|
|
|
714
2294
|
if (error) {
|
|
2295
|
+
const recordToken = javaRecordRecoveryToken(tree.topNode, source)
|
|
2296
|
+
|
|
2297
|
+
if (recordToken) return unsupportedSyntax("java", "Java record declaration under the pinned parser", nodeLocation(recordToken, filename, source))
|
|
715
2298
|
throw new SemantifoldDiagnostic({
|
|
716
2299
|
code: "PARSE_ERROR",
|
|
717
2300
|
language: "java",
|
|
@@ -720,16 +2303,341 @@ export function parseJava({filename, source}) {
|
|
|
720
2303
|
})
|
|
721
2304
|
}
|
|
722
2305
|
|
|
723
|
-
|
|
724
|
-
|
|
2306
|
+
if (program) return parseJavaProgramModule(tree.topNode, filename, source, program, capabilities)
|
|
2307
|
+
|
|
2308
|
+
const programMembers = task034JavaProgramMembers(structuralChildren(tree.topNode), capabilities, filename, source)
|
|
2309
|
+
const classDeclarations = programMembers.filter((member) => member.name == "ClassDeclaration")
|
|
2310
|
+
const unexpectedProgramMember = programMembers.find((member) => member.name != "ClassDeclaration")
|
|
2311
|
+
const mainClasses = classDeclarations.filter((declaration) => nodeText(requiredChild(declaration, "Definition", filename, source), source) == "Main")
|
|
2312
|
+
const classDeclaration = mainClasses[0]
|
|
2313
|
+
|
|
2314
|
+
if (!classDeclaration || mainClasses.length != 1 || unexpectedProgramMember || classDeclarations.at(-1) != classDeclaration) {
|
|
2315
|
+
return unsupportedSyntax("java", "compilation unit other than record classes followed by one Main class",
|
|
2316
|
+
nodeLocation(unexpectedProgramMember ?? tree.topNode, filename, source))
|
|
2317
|
+
}
|
|
2318
|
+
const className = requiredChild(classDeclaration, "Definition", filename, source)
|
|
2319
|
+
const classModifiers = classDeclaration.getChild("Modifiers")
|
|
2320
|
+
const classModifierNames = classModifiers ? structuralChildren(classModifiers).map((modifier) => modifier.name) : []
|
|
2321
|
+
|
|
2322
|
+
if (nodeText(className, source) != "Main" || classModifierNames.join(" ") != "public final") {
|
|
2323
|
+
return unsupportedSyntax("java", "public final Main class", nodeLocation(classDeclaration, filename, source))
|
|
2324
|
+
}
|
|
2325
|
+
const classBody = requiredChild(classDeclaration, "ClassBody", filename, source)
|
|
2326
|
+
const members = task034JavaMainMembers(structuralChildren(classBody), capabilities, filename, source)
|
|
2327
|
+
const unsupportedMember = members.find((member) => member.name != "MethodDeclaration")
|
|
2328
|
+
|
|
2329
|
+
if (unsupportedMember) return unsupportedSyntax("java", unsupportedMember.name, nodeLocation(unsupportedMember, filename, source))
|
|
2330
|
+
const methods = members
|
|
2331
|
+
const mainMethods = methods.filter((method) => nodeText(requiredChild(method, "Definition", filename, source), source) == "main")
|
|
2332
|
+
const mainMethod = mainMethods[0]
|
|
725
2333
|
const functionMethods = methods.filter((method) => method != mainMethod)
|
|
726
2334
|
const location = moduleLocation(filename, source)
|
|
727
2335
|
|
|
728
2336
|
if (functionMethods.length == 0) return unsupportedSyntax("java", "class without a semantic function", location)
|
|
729
|
-
if (!mainMethod) return unsupportedSyntax("java", "class without main", location)
|
|
2337
|
+
if (!mainMethod || mainMethods.length != 1) return unsupportedSyntax("java", "class without one unambiguous main", location)
|
|
730
2338
|
|
|
731
|
-
const
|
|
732
|
-
const
|
|
2339
|
+
const nominalNodes = classDeclarations.filter((declaration) => declaration != classDeclaration)
|
|
2340
|
+
const errorNodes = nominalNodes.filter((declaration) => {
|
|
2341
|
+
const parent = declaration.getChild("Superclass")
|
|
2342
|
+
return parent && nodeText(parent, source) == "extends RuntimeException"
|
|
2343
|
+
})
|
|
2344
|
+
const referenceClassNodes = nominalNodes.filter((declaration) => !errorNodes.includes(declaration) &&
|
|
2345
|
+
structuralChildren(requiredChild(declaration, "ClassBody", filename, source)).some((member) =>
|
|
2346
|
+
member.name == "FieldDeclaration" && nodeText(member.getChild("Modifiers") ?? member, source) == "private"))
|
|
2347
|
+
const recordNodes = nominalNodes.filter((declaration) => !errorNodes.includes(declaration) && !referenceClassNodes.includes(declaration))
|
|
2348
|
+
const errorDeclarations = errorNodes.map((errorNode, index) => ({
|
|
2349
|
+
id: `error:${index}`,
|
|
2350
|
+
kind: /** @type {const} */ ("ErrorDeclaration"),
|
|
2351
|
+
location: nodeLocation(errorNode, filename, source),
|
|
2352
|
+
name: nodeText(requiredChild(errorNode, "Definition", filename, source), source)
|
|
2353
|
+
}))
|
|
2354
|
+
const recordDeclarations = recordNodes.map((recordNode, index) => {
|
|
2355
|
+
const id = `record:${index}`
|
|
2356
|
+
const typeParameters = convertJavaTypeParameters(recordNode.getChild("TypeParameters"), id, filename, source)
|
|
2357
|
+
|
|
2358
|
+
return {
|
|
2359
|
+
fields: [],
|
|
2360
|
+
id,
|
|
2361
|
+
kind: /** @type {const} */ ("RecordDeclaration"),
|
|
2362
|
+
location: nodeLocation(recordNode, filename, source),
|
|
2363
|
+
name: nodeText(requiredChild(recordNode, "Definition", filename, source), source),
|
|
2364
|
+
...(typeParameters ? {typeParameters} : {})
|
|
2365
|
+
}
|
|
2366
|
+
})
|
|
2367
|
+
const referenceDeclarations = referenceClassNodes.map((classNode, index) => ({
|
|
2368
|
+
constructor: /** @type {import("../semantic/types.js").ConstructorDeclaration} */ ({}), fields: [], id: `class:${index}`,
|
|
2369
|
+
kind: /** @type {const} */ ("ClassDeclaration"), location: nodeLocation(classNode, filename, source), methods: [],
|
|
2370
|
+
name: nodeText(requiredChild(classNode, "Definition", filename, source), source)
|
|
2371
|
+
}))
|
|
2372
|
+
const recordNames = /** @type {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} */ (
|
|
2373
|
+
new Map(recordDeclarations.map((declaration) => /** @type {const} */ ([declaration.name, declaration]))))
|
|
2374
|
+
for (const resource of capabilities.flatMap(({resources}) => resources)) recordNames.set(resource.name, resource)
|
|
2375
|
+
for (const declaration of referenceDeclarations) recordNames.set(declaration.name, declaration)
|
|
2376
|
+
/** @type {Map<string, import("../semantic/types.js").ErrorDeclaration>} */
|
|
2377
|
+
const errorNames = new Map(errorDeclarations.map((declaration) => /** @type {const} */ ([declaration.name, declaration])))
|
|
2378
|
+
for (const failure of capabilities.flatMap(({failures}) => failures)) {
|
|
2379
|
+
errorNames.set(failure.name, /** @type {import("../semantic/types.js").ErrorDeclaration} */ (/** @type {unknown} */ (failure)))
|
|
2380
|
+
}
|
|
2381
|
+
const errorsById = new Map([...errorNames.values()].map((declaration) => [/** @type {string} */ (declaration.id), declaration]))
|
|
2382
|
+
const errors = errorNodes.map((errorNode, index) => convertJavaError(errorNode, errorDeclarations[index], filename, source))
|
|
2383
|
+
const recordsById = new Map(recordDeclarations.map((declaration) => [/** @type {string} */ (declaration.id), declaration]))
|
|
2384
|
+
const classesById = new Map(referenceDeclarations.map((declaration) => [/** @type {string} */ (declaration.id), declaration]))
|
|
2385
|
+
const records = recordNodes.map((recordNode, index) =>
|
|
2386
|
+
convertJavaRecord(recordNode, recordDeclarations[index],
|
|
2387
|
+
/** @type {Map<string, import("../semantic/types.js").RecordDeclaration>} */ (/** @type {unknown} */ (recordNames)), filename, source))
|
|
2388
|
+
const signatures = functionMethods.map((method, index) =>
|
|
2389
|
+
convertFunctionSignature(method, filename, source, recordNames, false, `function:${index}`, referenceDeclarations.length > 0))
|
|
2390
|
+
/** @type {Map<string, JavaFunctionSignature>} */
|
|
2391
|
+
const functionSignatures = new Map(capabilityFunctionSignatures(capabilities, location))
|
|
2392
|
+
for (const signature of signatures) functionSignatures.set(signature.name, signature)
|
|
2393
|
+
const classes = referenceClassNodes.map((classNode, index) => convertJavaReferenceClass(classNode, referenceDeclarations[index],
|
|
2394
|
+
recordNames, recordsById, classesById, functionSignatures, errorNames, errorsById, filename, source))
|
|
2395
|
+
const functions = functionMethods.map((method, index) =>
|
|
2396
|
+
convertFunction(method, signatures[index], functionSignatures, recordNames, recordsById, classesById,
|
|
2397
|
+
errorNames, errorsById, filename, source))
|
|
2398
|
+
const entryPoint = convertEntryPoint(mainMethod, filename, source, functionSignatures, recordNames, recordsById,
|
|
2399
|
+
classesById, errorNames, errorsById)
|
|
2400
|
+
|
|
2401
|
+
return {entryPoint, functions, kind: "Module", location, ...(errors.length > 0 ? {errors} : {}),
|
|
2402
|
+
...(classes.length > 0 ? {classes} : {}), ...(records.length > 0 ? {records} : {})}
|
|
2403
|
+
}
|
|
2404
|
+
|
|
2405
|
+
/**
|
|
2406
|
+
* Removes the exact protected top-level support emitted for the built-in probe.
|
|
2407
|
+
* @param {import("@lezer/common").SyntaxNode[]} members - Parsed program members.
|
|
2408
|
+
* @param {readonly import("../semantic/types.js").EffectCapabilityDeclaration[]} capabilities - Authorized declarations.
|
|
2409
|
+
* @param {string} filename - Source filename.
|
|
2410
|
+
* @param {string} source - Complete source.
|
|
2411
|
+
* @returns {import("@lezer/common").SyntaxNode[]} Portable program members.
|
|
2412
|
+
*/
|
|
2413
|
+
function task034JavaProgramMembers(members, capabilities, filename, source) {
|
|
2414
|
+
if (!isTask034ResourceProbe(capabilities) || nodeText(members[0], source) != "// semantifold-task034-support") return members
|
|
2415
|
+
if (!hasExactEffectSupport(source, "java", "top")) {
|
|
2416
|
+
return unsupportedSyntax("java", "malformed protected Task 034 top-level support", nodeLocation(members[0], filename, source))
|
|
2417
|
+
}
|
|
2418
|
+
const names = ["ProbeOperationFailure", "ProbeAcquireFailure", "ProbeReadFailure", "ProbeCloseFailure",
|
|
2419
|
+
"ProbeResourceClosed", "ProbeResource"]
|
|
2420
|
+
const matches = names.every((name, index) => members[index + 1]?.name == "ClassDeclaration" &&
|
|
2421
|
+
nodeText(requiredChild(members[index + 1], "Definition", filename, source), source) == name)
|
|
2422
|
+
|
|
2423
|
+
if (!matches || nodeText(members[names.length + 1], source) != "// semantifold-task034-support-top-end") {
|
|
2424
|
+
return unsupportedSyntax("java", "malformed protected Task 034 top-level support", nodeLocation(members[0], filename, source))
|
|
2425
|
+
}
|
|
2426
|
+
return members.slice(names.length + 2)
|
|
2427
|
+
}
|
|
2428
|
+
|
|
2429
|
+
/**
|
|
2430
|
+
* Removes the exact protected Main-member support emitted for the built-in probe.
|
|
2431
|
+
* @param {import("@lezer/common").SyntaxNode[]} members - Parsed Main members.
|
|
2432
|
+
* @param {readonly import("../semantic/types.js").EffectCapabilityDeclaration[]} capabilities - Authorized declarations.
|
|
2433
|
+
* @param {string} filename - Source filename.
|
|
2434
|
+
* @param {string} source - Complete source.
|
|
2435
|
+
* @returns {import("@lezer/common").SyntaxNode[]} Portable Main members.
|
|
2436
|
+
*/
|
|
2437
|
+
function task034JavaMainMembers(members, capabilities, filename, source) {
|
|
2438
|
+
if (!isTask034ResourceProbe(capabilities) || nodeText(members[0], source) != "// semantifold-task034-support-members") return members
|
|
2439
|
+
if (!hasExactEffectSupport(source, "java", "members")) {
|
|
2440
|
+
return unsupportedSyntax("java", "malformed protected Task 034 member support", nodeLocation(members[0], filename, source))
|
|
2441
|
+
}
|
|
2442
|
+
const expected = [
|
|
2443
|
+
["FieldDeclaration", "__semantifoldTask034Trace"], ["MethodDeclaration", "probeEffect"],
|
|
2444
|
+
["MethodDeclaration", "probeAcquire"], ["MethodDeclaration", "probeRead"],
|
|
2445
|
+
["MethodDeclaration", "probeClose"], ["MethodDeclaration", "probeTrace"]
|
|
2446
|
+
]
|
|
2447
|
+
const matches = expected.every(([kind, name], index) => {
|
|
2448
|
+
const member = members[index + 1]
|
|
2449
|
+
if (member?.name != kind) return false
|
|
2450
|
+
if (kind == "FieldDeclaration") return nodeText(member, source).includes(` ${name} =`)
|
|
2451
|
+
return nodeText(requiredChild(member, "Definition", filename, source), source) == name
|
|
2452
|
+
})
|
|
2453
|
+
|
|
2454
|
+
if (!matches || nodeText(members[expected.length + 1], source) != "// semantifold-task034-support-members-end") {
|
|
2455
|
+
return unsupportedSyntax("java", "malformed protected Task 034 member support", nodeLocation(members[0], filename, source))
|
|
2456
|
+
}
|
|
2457
|
+
return members.slice(expected.length + 2)
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
/**
|
|
2461
|
+
* Converts one already-qualified canonical Java compilation unit.
|
|
2462
|
+
* @param {import("@lezer/common").SyntaxNode} root - Error-free Program node.
|
|
2463
|
+
* @param {string} filename - Source filename.
|
|
2464
|
+
* @param {string} source - Complete source text.
|
|
2465
|
+
* @param {{isEntry: boolean, functions: Map<string, import("../semantic/types.js").FunctionDeclaration>, records: Map<string, import("../semantic/types.js").RecordDeclaration>, errors?: Map<string, import("../semantic/types.js").ErrorDeclaration>}} program - Resolved program imports and entry role.
|
|
2466
|
+
* @param {readonly import("../semantic/types.js").EffectCapabilityDeclaration[]} capabilities - Compiler-authorized declarations.
|
|
2467
|
+
* @returns {import("../semantic/types.js").SemanticModule} Semantic module.
|
|
2468
|
+
*/
|
|
2469
|
+
function parseJavaProgramModule(root, filename, source, program, capabilities) {
|
|
2470
|
+
inspectJavaModule({filename, source})
|
|
2471
|
+
const classDeclaration = /** @type {import("@lezer/common").SyntaxNode} */ (structuralChildren(root).find((node) => node.name == "ClassDeclaration"))
|
|
2472
|
+
const classModifiers = classDeclaration.getChild("Modifiers")
|
|
2473
|
+
const classModifierNames = classModifiers ? structuralChildren(classModifiers).map(({name}) => name) : []
|
|
2474
|
+
const classBody = requiredChild(classDeclaration, "ClassBody", filename, source)
|
|
2475
|
+
const members = structuralChildren(classBody)
|
|
2476
|
+
const mainMethods = members.filter((method) => method.name == "MethodDeclaration" &&
|
|
2477
|
+
nodeText(requiredChild(method, "Definition", filename, source), source) == "main")
|
|
2478
|
+
const superclass = classDeclaration.getChild("Superclass")
|
|
2479
|
+
const isError = Boolean(superclass && nodeText(superclass, source) == "extends RuntimeException")
|
|
2480
|
+
const isRecord = !isError && members.some((member) => member.name == "FieldDeclaration" || member.name == "ConstructorDeclaration")
|
|
2481
|
+
const location = moduleLocation(filename, source)
|
|
2482
|
+
|
|
2483
|
+
if (program.isEntry && classModifierNames.join(" ") != "public final") {
|
|
2484
|
+
return unsupportedSyntax("java", "selected entry class without public final visibility", nodeLocation(classDeclaration, filename, source))
|
|
2485
|
+
}
|
|
2486
|
+
if (program.isEntry && mainMethods.length != 1) return unsupportedSyntax("java", "selected entry class without one main method", location)
|
|
2487
|
+
if (!program.isEntry && mainMethods.length > 0) return unsupportedSyntax("java", "main method outside the selected entry module", nodeLocation(mainMethods[0], filename, source))
|
|
2488
|
+
if ((isRecord || isError) && mainMethods.length > 0) {
|
|
2489
|
+
return unsupportedSyntax("java", "nominal class containing main", nodeLocation(classDeclaration, filename, source))
|
|
2490
|
+
}
|
|
2491
|
+
|
|
2492
|
+
const errorDeclarations = isError ? [{
|
|
2493
|
+
id: "error:0",
|
|
2494
|
+
kind: /** @type {const} */ ("ErrorDeclaration"),
|
|
2495
|
+
location: nodeLocation(classDeclaration, filename, source),
|
|
2496
|
+
name: nodeText(requiredChild(classDeclaration, "Definition", filename, source), source)
|
|
2497
|
+
}] : []
|
|
2498
|
+
|
|
2499
|
+
const recordTypeParameters = isRecord
|
|
2500
|
+
? convertJavaTypeParameters(classDeclaration.getChild("TypeParameters"), "record:0", filename, source)
|
|
2501
|
+
: undefined
|
|
2502
|
+
const recordDeclarations = isRecord ? [{
|
|
2503
|
+
fields: [],
|
|
2504
|
+
id: "record:0",
|
|
2505
|
+
kind: /** @type {const} */ ("RecordDeclaration"),
|
|
2506
|
+
location: nodeLocation(classDeclaration, filename, source),
|
|
2507
|
+
name: nodeText(requiredChild(classDeclaration, "Definition", filename, source), source),
|
|
2508
|
+
...(recordTypeParameters ? {typeParameters: recordTypeParameters} : {})
|
|
2509
|
+
}] : []
|
|
2510
|
+
/** @type {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} */
|
|
2511
|
+
const recordNames = new Map(program.records)
|
|
2512
|
+
const errorNames = new Map(program.errors ?? [])
|
|
2513
|
+
|
|
2514
|
+
for (const resource of capabilities.flatMap(({resources}) => resources)) recordNames.set(resource.name, resource)
|
|
2515
|
+
for (const failure of capabilities.flatMap(({failures}) => failures)) {
|
|
2516
|
+
errorNames.set(failure.name, /** @type {import("../semantic/types.js").ErrorDeclaration} */ (/** @type {unknown} */ (failure)))
|
|
2517
|
+
}
|
|
2518
|
+
for (const declaration of recordDeclarations) recordNames.set(declaration.name, declaration)
|
|
2519
|
+
for (const declaration of errorDeclarations) errorNames.set(declaration.name, declaration)
|
|
2520
|
+
const errorsById = new Map([...errorNames.values()].map((declaration) => [/** @type {string} */ (declaration.id), declaration]))
|
|
2521
|
+
const errors = errorDeclarations.map((declaration) => convertJavaError(classDeclaration, declaration, filename, source, true))
|
|
2522
|
+
const recordsById = new Map([...program.records.values(), ...recordDeclarations]
|
|
2523
|
+
.map((declaration) => [/** @type {string} */ (declaration.id), declaration]))
|
|
2524
|
+
const classesById = new Map()
|
|
2525
|
+
const records = recordDeclarations.map((declaration) =>
|
|
2526
|
+
convertJavaRecord(classDeclaration, declaration,
|
|
2527
|
+
/** @type {Map<string, import("../semantic/types.js").RecordDeclaration>} */ (/** @type {unknown} */ (recordNames)),
|
|
2528
|
+
filename, source, true))
|
|
2529
|
+
const functionMethods = isRecord || isError ? [] : members.filter((member) => member.name == "MethodDeclaration" && !mainMethods.includes(member))
|
|
2530
|
+
const signatures = functionMethods.map((method, index) =>
|
|
2531
|
+
convertFunctionSignature(method, filename, source, recordNames, true, `function:${index}`))
|
|
2532
|
+
/** @type {[string, JavaFunctionSignature][]} */
|
|
2533
|
+
const importedFunctionSignatures = [...program.functions].map(([localName, declaration]) => [localName, {
|
|
2534
|
+
location: declaration.location,
|
|
2535
|
+
name: localName,
|
|
2536
|
+
nameLocation: declaration.location,
|
|
2537
|
+
parameters: declaration.parameters,
|
|
2538
|
+
returnType: declaration.returnType,
|
|
2539
|
+
...(declaration.typeParameters ? {typeParameters: declaration.typeParameters} : {})
|
|
2540
|
+
}])
|
|
2541
|
+
const functionSignatures = new Map([...capabilityFunctionSignatures(capabilities, location), ...importedFunctionSignatures])
|
|
2542
|
+
|
|
2543
|
+
for (const signature of signatures) functionSignatures.set(signature.name, signature)
|
|
2544
|
+
const functions = functionMethods.map((method, index) =>
|
|
2545
|
+
convertFunction(method, signatures[index], functionSignatures, recordNames, recordsById, classesById,
|
|
2546
|
+
errorNames, errorsById, filename, source))
|
|
2547
|
+
const emptyBlock = {kind: /** @type {const} */ ("Block"), location, statements: []}
|
|
2548
|
+
const entryPoint = program.isEntry
|
|
2549
|
+
? convertEntryPoint(mainMethods[0], filename, source, functionSignatures, recordNames, recordsById, classesById,
|
|
2550
|
+
errorNames, errorsById)
|
|
2551
|
+
: {body: emptyBlock, kind: /** @type {const} */ ("EntryPoint"), location}
|
|
2552
|
+
|
|
2553
|
+
return {
|
|
2554
|
+
entryPoint,
|
|
2555
|
+
functions,
|
|
2556
|
+
kind: "Module",
|
|
2557
|
+
location,
|
|
2558
|
+
...(errors.length > 0 ? {errors} : {}),
|
|
2559
|
+
...(records.length > 0 ? {records} : {})
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
/**
|
|
2564
|
+
* Reads direct package/import/class nodes from the pinned Lezer grammar.
|
|
2565
|
+
* @param {object} input - Parser input.
|
|
2566
|
+
* @param {string} input.filename - Source filename.
|
|
2567
|
+
* @param {string} input.source - Source text.
|
|
2568
|
+
* @returns {{imports: {importedName: string, localName: string, location: import("../semantic/types.js").SourceLocation, namespace: true, pathLocation: import("../semantic/types.js").SourceLocation, specifier: string, typeOnly: false}[], exports: {exportedName: string, localName: string, location: import("../semantic/types.js").SourceLocation, typeOnly: false}[], nativeName: string}} Parser-owned Java module header.
|
|
2569
|
+
*/
|
|
2570
|
+
export function inspectJavaModule({filename, source}) {
|
|
2571
|
+
const tree = parser.parse(source)
|
|
2572
|
+
const error = descendants(tree.topNode, "⚠")[0]
|
|
2573
|
+
|
|
2574
|
+
if (error) {
|
|
2575
|
+
throw new SemantifoldDiagnostic({
|
|
2576
|
+
code: "PARSE_ERROR",
|
|
2577
|
+
language: "java",
|
|
2578
|
+
location: nodeLocation(error, filename, source),
|
|
2579
|
+
message: "Java parser reported invalid syntax."
|
|
2580
|
+
})
|
|
2581
|
+
}
|
|
2582
|
+
const members = structuralChildren(tree.topNode)
|
|
2583
|
+
const packages = members.filter((node) => node.name == "PackageDeclaration")
|
|
2584
|
+
const importsNodes = members.filter((node) => node.name == "ImportDeclaration")
|
|
2585
|
+
const classes = members.filter((node) => node.name == "ClassDeclaration")
|
|
2586
|
+
const invalid = members.find((node) => !["PackageDeclaration", "ImportDeclaration", "ClassDeclaration"].includes(node.name))
|
|
2587
|
+
const packageNode = packages[0]
|
|
2588
|
+
const classNode = classes[0]
|
|
2589
|
+
|
|
2590
|
+
if (!packageNode || packages.length != 1 || !classNode || classes.length != 1 || invalid ||
|
|
2591
|
+
members.indexOf(packageNode) != 0 || members.indexOf(classNode) != members.length - 1) {
|
|
2592
|
+
return unsupportedSyntax("java", "one package followed by imports and one public class",
|
|
2593
|
+
nodeLocation(invalid ?? packages[1] ?? classes[1] ?? tree.topNode, filename, source))
|
|
2594
|
+
}
|
|
2595
|
+
const packageNameNode = packageNode.getChild("ScopedIdentifier") ?? packageNode.getChild("Identifier")
|
|
2596
|
+
const classNameNode = requiredChild(classNode, "Definition", filename, source)
|
|
2597
|
+
const modifiers = classNode.getChild("Modifiers")
|
|
2598
|
+
const modifierNames = modifiers ? structuralChildren(modifiers).map(({name}) => name) : []
|
|
2599
|
+
const packageName = packageNameNode ? nodeText(packageNameNode, source) : ""
|
|
2600
|
+
const className = nodeText(classNameNode, source)
|
|
2601
|
+
const expectedSuffix = `${packageName.replaceAll(".", "/")}/${className}.java`
|
|
2602
|
+
|
|
2603
|
+
if (!packageNameNode || !["public final", "final"].includes(modifierNames.join(" ")) || !filename.endsWith(expectedSuffix)) {
|
|
2604
|
+
return unsupportedSyntax("java", "package/path/public-class contract", nodeLocation(classNode, filename, source))
|
|
2605
|
+
}
|
|
2606
|
+
const imports = importsNodes.map((node) => {
|
|
2607
|
+
const name = node.getChild("ScopedIdentifier") ?? node.getChild("Identifier")
|
|
2608
|
+
|
|
2609
|
+
if (!name || node.getChild("static") || node.getChild("Asterisk")) {
|
|
2610
|
+
return unsupportedSyntax("java", "static or wildcard import", nodeLocation(node, filename, source))
|
|
2611
|
+
}
|
|
2612
|
+
const specifier = nodeText(name, source)
|
|
2613
|
+
|
|
2614
|
+
return {
|
|
2615
|
+
importedName: "*",
|
|
2616
|
+
localName: specifier.split(".").at(-1) ?? "",
|
|
2617
|
+
location: nodeLocation(node, filename, source),
|
|
2618
|
+
namespace: /** @type {const} */ (true),
|
|
2619
|
+
pathLocation: nodeLocation(name, filename, source),
|
|
2620
|
+
specifier,
|
|
2621
|
+
typeOnly: /** @type {const} */ (false)
|
|
2622
|
+
}
|
|
2623
|
+
})
|
|
2624
|
+
const classBody = requiredChild(classNode, "ClassBody", filename, source)
|
|
2625
|
+
const classMembers = structuralChildren(classBody)
|
|
2626
|
+
const recordClass = classMembers.some((member) => member.name == "FieldDeclaration" || member.name == "ConstructorDeclaration")
|
|
2627
|
+
const exports = recordClass
|
|
2628
|
+
? modifierNames.join(" ") == "public final"
|
|
2629
|
+
? [{exportedName: className, localName: className, location: nodeLocation(classNameNode, filename, source), typeOnly: /** @type {const} */ (false)}]
|
|
2630
|
+
: []
|
|
2631
|
+
: modifierNames.join(" ") != "public final" ? [] : classMembers.flatMap((member) => {
|
|
2632
|
+
if (member.name != "MethodDeclaration") return []
|
|
2633
|
+
const name = requiredChild(member, "Definition", filename, source)
|
|
2634
|
+
const methodModifiers = member.getChild("Modifiers")
|
|
2635
|
+
const methodModifierNames = methodModifiers ? structuralChildren(methodModifiers).map(({name: modifierName}) => modifierName) : []
|
|
2636
|
+
|
|
2637
|
+
if (nodeText(name, source) == "main" || methodModifierNames.join(" ") == "private static") return []
|
|
2638
|
+
|
|
2639
|
+
return [{exportedName: nodeText(name, source), localName: nodeText(name, source), location: nodeLocation(name, filename, source), typeOnly: /** @type {const} */ (false)}]
|
|
2640
|
+
})
|
|
733
2641
|
|
|
734
|
-
return {
|
|
2642
|
+
return {exports, imports, nativeName: `${packageName}.${className}`}
|
|
735
2643
|
}
|