goscript 0.0.84 → 0.1.1
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 +267 -243
- package/cmd/goscript/cmd-test.go +193 -0
- package/cmd/goscript/cmd-test_test.go +76 -0
- package/cmd/goscript/cmd_compile.go +70 -69
- package/cmd/goscript/cmd_compile_test.go +79 -0
- package/cmd/goscript/main.go +11 -5
- package/compiler/build-flags.go +38 -0
- package/compiler/compile-request.go +220 -0
- package/compiler/compiler.go +16 -1336
- package/compiler/compliance_test.go +188 -0
- package/compiler/config.go +6 -13
- package/compiler/diagnostic.go +70 -0
- package/compiler/gotest/owner.go +24 -0
- package/compiler/gotest/package-result.go +67 -0
- package/compiler/gotest/request.go +145 -0
- package/compiler/gotest/result.go +28 -0
- package/compiler/gotest/runner.go +588 -0
- package/compiler/gotest/runner_test.go +627 -0
- package/compiler/gotest/test.go +9 -0
- package/compiler/index.test.ts +28 -28
- package/compiler/index.ts +40 -72
- package/compiler/lowered-program.go +184 -0
- package/compiler/lowering.go +8072 -0
- package/compiler/override-facts.go +307 -0
- package/compiler/override-registry.go +283 -0
- package/compiler/override-registry_test.go +254 -0
- package/compiler/package-graph.go +254 -0
- package/compiler/package-graph_test.go +316 -0
- package/compiler/package-test-function.go +9 -0
- package/compiler/package-test-graph-package.go +40 -0
- package/compiler/package-test-graph-variant.go +105 -0
- package/compiler/package-test-graph.go +117 -0
- package/compiler/package-test-graph_test.go +144 -0
- package/compiler/result.go +13 -0
- package/compiler/runtime-contract.go +439 -0
- package/compiler/runtime-contract_test.go +104 -0
- package/compiler/semantic-model-types.go +113 -0
- package/compiler/semantic-model.go +1422 -0
- package/compiler/semantic-model_test.go +471 -0
- package/compiler/service.go +133 -0
- package/compiler/skeleton_test.go +1775 -0
- package/compiler/tsworkspace/owner.go +334 -0
- package/compiler/tsworkspace/owner_test.go +93 -0
- package/compiler/tsworkspace/result.go +17 -0
- package/compiler/typescript-emitter.go +1040 -0
- package/compiler/wasm/compile.go +2 -3
- package/compiler/wasm/compile_test.go +79 -0
- package/compiler/wasm_api.go +140 -124
- package/dist/compiler/index.d.ts +1 -3
- package/dist/compiler/index.js +31 -55
- package/dist/compiler/index.js.map +1 -1
- package/dist/gs/builtin/builtin.d.ts +33 -2
- package/dist/gs/builtin/builtin.js +217 -6
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/channel.d.ts +11 -3
- package/dist/gs/builtin/channel.js +12 -0
- package/dist/gs/builtin/channel.js.map +1 -1
- package/dist/gs/builtin/hostio.d.ts +15 -1
- package/dist/gs/builtin/hostio.js +134 -49
- package/dist/gs/builtin/hostio.js.map +1 -1
- package/dist/gs/builtin/index.d.ts +1 -0
- package/dist/gs/builtin/index.js +1 -0
- package/dist/gs/builtin/index.js.map +1 -1
- package/dist/gs/builtin/slice.d.ts +23 -3
- package/dist/gs/builtin/slice.js +216 -44
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/builtin/type.d.ts +16 -2
- package/dist/gs/builtin/type.js +134 -21
- package/dist/gs/builtin/type.js.map +1 -1
- package/dist/gs/builtin/varRef.d.ts +5 -0
- package/dist/gs/builtin/varRef.js +23 -0
- package/dist/gs/builtin/varRef.js.map +1 -1
- package/dist/gs/bytes/buffer.gs.js +48 -44
- package/dist/gs/bytes/buffer.gs.js.map +1 -1
- package/dist/gs/bytes/bytes.gs.js.map +1 -1
- package/dist/gs/bytes/reader.gs.js +20 -18
- package/dist/gs/bytes/reader.gs.js.map +1 -1
- package/dist/gs/context/context.d.ts +5 -4
- package/dist/gs/context/context.js +10 -10
- package/dist/gs/context/context.js.map +1 -1
- package/dist/gs/crypto/internal/fips140deps/byteorder/index.d.ts +1 -0
- package/dist/gs/crypto/internal/fips140deps/byteorder/index.js +2 -0
- package/dist/gs/crypto/internal/fips140deps/byteorder/index.js.map +1 -0
- package/dist/gs/crypto/internal/fips140deps/godebug/index.d.ts +1 -0
- package/dist/gs/crypto/internal/fips140deps/godebug/index.js +2 -0
- package/dist/gs/crypto/internal/fips140deps/godebug/index.js.map +1 -0
- package/dist/gs/crypto/rand/index.d.ts +5 -0
- package/dist/gs/crypto/rand/index.js +77 -0
- package/dist/gs/crypto/rand/index.js.map +1 -0
- package/dist/gs/embed/index.d.ts +7 -0
- package/dist/gs/embed/index.js +16 -0
- package/dist/gs/embed/index.js.map +1 -0
- package/dist/gs/encoding/json/index.d.ts +4 -0
- package/dist/gs/encoding/json/index.js +178 -0
- package/dist/gs/encoding/json/index.js.map +1 -0
- package/dist/gs/errors/errors.d.ts +4 -0
- package/dist/gs/errors/errors.js +81 -0
- package/dist/gs/errors/errors.js.map +1 -1
- package/dist/gs/fmt/fmt.d.ts +4 -4
- package/dist/gs/fmt/fmt.js +42 -11
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +36 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +212 -2
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.d.ts +189 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js +825 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js.map +1 -0
- package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.d.ts +163 -0
- package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js +449 -0
- package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js.map +1 -0
- package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js.map +1 -1
- package/dist/gs/github.com/klauspost/compress/internal/le/index.d.ts +9 -0
- package/dist/gs/github.com/klauspost/compress/internal/le/index.js +71 -0
- package/dist/gs/github.com/klauspost/compress/internal/le/index.js.map +1 -0
- package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
- package/dist/gs/github.com/pkg/errors/stack.js.map +1 -1
- package/dist/gs/go/internal/scannerhooks/index.d.ts +3 -0
- package/dist/gs/go/internal/scannerhooks/index.js +5 -0
- package/dist/gs/go/internal/scannerhooks/index.js.map +1 -0
- package/dist/gs/go/scanner/index.d.ts +42 -0
- package/dist/gs/go/scanner/index.js +155 -0
- package/dist/gs/go/scanner/index.js.map +1 -0
- package/dist/gs/go/token/index.d.ts +187 -0
- package/dist/gs/go/token/index.js +578 -0
- package/dist/gs/go/token/index.js.map +1 -0
- package/dist/gs/internal/abi/index.d.ts +4 -0
- package/dist/gs/internal/abi/index.js +10 -0
- package/dist/gs/internal/abi/index.js.map +1 -1
- package/dist/gs/internal/bytealg/index.d.ts +2 -0
- package/dist/gs/internal/bytealg/index.js +14 -0
- package/dist/gs/internal/bytealg/index.js.map +1 -1
- package/dist/gs/internal/byteorder/index.d.ts +8 -2
- package/dist/gs/internal/byteorder/index.js +56 -25
- package/dist/gs/internal/byteorder/index.js.map +1 -1
- package/dist/gs/internal/godebug/index.d.ts +12 -0
- package/dist/gs/internal/godebug/index.js +30 -0
- package/dist/gs/internal/godebug/index.js.map +1 -0
- package/dist/gs/io/fs/fs.js.map +1 -1
- package/dist/gs/io/fs/index.d.ts +1 -0
- package/dist/gs/io/fs/index.js +1 -0
- package/dist/gs/io/fs/index.js.map +1 -1
- package/dist/gs/io/fs/readdir.js.map +1 -1
- package/dist/gs/io/fs/readfile.js.map +1 -1
- package/dist/gs/io/fs/readlink.d.ts +8 -0
- package/dist/gs/io/fs/readlink.js +64 -0
- package/dist/gs/io/fs/readlink.js.map +1 -0
- package/dist/gs/io/fs/stat.js.map +1 -1
- package/dist/gs/io/fs/sub.js.map +1 -1
- package/dist/gs/io/fs/walk.d.ts +3 -3
- package/dist/gs/io/fs/walk.js +7 -7
- package/dist/gs/io/fs/walk.js.map +1 -1
- package/dist/gs/io/io.d.ts +40 -6
- package/dist/gs/io/io.js +151 -26
- package/dist/gs/io/io.js.map +1 -1
- package/dist/gs/maps/iter.d.ts +3 -3
- package/dist/gs/maps/iter.js +3 -3
- package/dist/gs/maps/iter.js.map +1 -1
- package/dist/gs/maps/maps.d.ts +2 -2
- package/dist/gs/maps/maps.js +1 -1
- package/dist/gs/maps/maps.js.map +1 -1
- package/dist/gs/math/bits/index.d.ts +13 -4
- package/dist/gs/math/bits/index.js +66 -34
- package/dist/gs/math/bits/index.js.map +1 -1
- package/dist/gs/math/const.gs.d.ts +5 -5
- package/dist/gs/math/const.gs.js +4 -4
- package/dist/gs/math/const.gs.js.map +1 -1
- package/dist/gs/mime/index.d.ts +1 -0
- package/dist/gs/mime/index.js +50 -0
- package/dist/gs/mime/index.js.map +1 -0
- package/dist/gs/net/http/httptest/index.d.ts +11 -0
- package/dist/gs/net/http/httptest/index.js +21 -0
- package/dist/gs/net/http/httptest/index.js.map +1 -0
- package/dist/gs/net/http/index.d.ts +27 -0
- package/dist/gs/net/http/index.js +61 -0
- package/dist/gs/net/http/index.js.map +1 -0
- package/dist/gs/os/dir_unix.gs.js +2 -2
- package/dist/gs/os/dir_unix.gs.js.map +1 -1
- package/dist/gs/os/error.gs.js +2 -4
- package/dist/gs/os/error.gs.js.map +1 -1
- package/dist/gs/os/exec.gs.js.map +1 -1
- package/dist/gs/os/exec_posix.gs.js.map +1 -1
- package/dist/gs/os/rawconn_js.gs.js.map +1 -1
- package/dist/gs/os/root_js.gs.js.map +1 -1
- package/dist/gs/os/tempfile.gs.js +66 -9
- package/dist/gs/os/tempfile.gs.js.map +1 -1
- package/dist/gs/os/types.gs.js.map +1 -1
- package/dist/gs/os/types_js.gs.js +9 -9
- package/dist/gs/os/types_js.gs.js.map +1 -1
- package/dist/gs/os/types_unix.gs.js.map +1 -1
- package/dist/gs/path/filepath/match.js +165 -3
- package/dist/gs/path/filepath/match.js.map +1 -1
- package/dist/gs/path/filepath/path.d.ts +3 -1
- package/dist/gs/path/filepath/path.js +133 -4
- package/dist/gs/path/filepath/path.js.map +1 -1
- package/dist/gs/path/match.js.map +1 -1
- package/dist/gs/path/path.d.ts +4 -1
- package/dist/gs/path/path.js +16 -4
- package/dist/gs/path/path.js.map +1 -1
- package/dist/gs/reflect/index.d.ts +3 -3
- package/dist/gs/reflect/index.js +2 -2
- package/dist/gs/reflect/index.js.map +1 -1
- package/dist/gs/reflect/map.js +3 -0
- package/dist/gs/reflect/map.js.map +1 -1
- package/dist/gs/reflect/type.d.ts +9 -5
- package/dist/gs/reflect/type.js +233 -21
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/reflect/types.js.map +1 -1
- package/dist/gs/reflect/visiblefields.js.map +1 -1
- package/dist/gs/runtime/debug/index.d.ts +2 -0
- package/dist/gs/runtime/debug/index.js +8 -0
- package/dist/gs/runtime/debug/index.js.map +1 -0
- package/dist/gs/runtime/runtime.d.ts +35 -3
- package/dist/gs/runtime/runtime.js +72 -0
- package/dist/gs/runtime/runtime.js.map +1 -1
- package/dist/gs/slices/slices.d.ts +24 -5
- package/dist/gs/slices/slices.js +214 -5
- package/dist/gs/slices/slices.js.map +1 -1
- package/dist/gs/sort/slice.gs.d.ts +3 -3
- package/dist/gs/sort/slice.gs.js +6 -6
- package/dist/gs/sort/slice.gs.js.map +1 -1
- package/dist/gs/sort/sort.gs.d.ts +4 -4
- package/dist/gs/sort/sort.gs.js +11 -8
- package/dist/gs/sort/sort.gs.js.map +1 -1
- package/dist/gs/strconv/atoi.gs.js.map +1 -1
- package/dist/gs/strconv/quote.gs.js.map +1 -1
- package/dist/gs/strings/builder.d.ts +1 -1
- package/dist/gs/strings/builder.js +3 -2
- package/dist/gs/strings/builder.js.map +1 -1
- package/dist/gs/strings/reader.js.map +1 -1
- package/dist/gs/strings/replace.js.map +1 -1
- package/dist/gs/sync/atomic/type.gs.d.ts +9 -8
- package/dist/gs/sync/atomic/type.gs.js +0 -2
- package/dist/gs/sync/atomic/type.gs.js.map +1 -1
- package/dist/gs/sync/atomic/value.gs.js.map +1 -1
- package/dist/gs/sync/sync.d.ts +3 -0
- package/dist/gs/sync/sync.js +39 -0
- package/dist/gs/sync/sync.js.map +1 -1
- package/dist/gs/syscall/constants.d.ts +36 -24
- package/dist/gs/syscall/constants.js +12 -0
- package/dist/gs/syscall/constants.js.map +1 -1
- package/dist/gs/syscall/errors.d.ts +2 -0
- package/dist/gs/syscall/errors.js +8 -0
- package/dist/gs/syscall/errors.js.map +1 -1
- package/dist/gs/syscall/fs.d.ts +43 -0
- package/dist/gs/syscall/fs.js +102 -0
- package/dist/gs/syscall/fs.js.map +1 -1
- package/dist/gs/syscall/js/index.d.ts +90 -0
- package/dist/gs/syscall/js/index.js +375 -0
- package/dist/gs/syscall/js/index.js.map +1 -0
- package/dist/gs/syscall/types.d.ts +22 -0
- package/dist/gs/syscall/types.js +45 -1
- package/dist/gs/syscall/types.js.map +1 -1
- package/dist/gs/testing/index.d.ts +1 -0
- package/dist/gs/testing/index.js +2 -0
- package/dist/gs/testing/index.js.map +1 -0
- package/dist/gs/testing/testing.d.ts +77 -0
- package/dist/gs/testing/testing.js +301 -0
- package/dist/gs/testing/testing.js.map +1 -0
- package/dist/gs/time/time.d.ts +41 -4
- package/dist/gs/time/time.js +205 -36
- package/dist/gs/time/time.js.map +1 -1
- package/dist/gs/unicode/unicode.d.ts +23 -1
- package/dist/gs/unicode/unicode.js +79 -10
- package/dist/gs/unicode/unicode.js.map +1 -1
- package/dist/gs/unicode/utf8/utf8.d.ts +4 -4
- package/dist/gs/unicode/utf8/utf8.js +24 -11
- package/dist/gs/unicode/utf8/utf8.js.map +1 -1
- package/dist/gs/unique/index.d.ts +11 -0
- package/dist/gs/unique/index.js +71 -0
- package/dist/gs/unique/index.js.map +1 -0
- package/go.mod +2 -2
- package/go.sum +9 -0
- package/gs/builtin/builtin.ts +266 -8
- package/gs/builtin/channel.ts +22 -0
- package/gs/builtin/hostio.test.ts +177 -0
- package/gs/builtin/hostio.ts +171 -56
- package/gs/builtin/index.ts +1 -0
- package/gs/builtin/runtime-contract.test.ts +356 -0
- package/gs/builtin/slice.ts +259 -50
- package/gs/builtin/type.ts +188 -30
- package/gs/builtin/varRef.ts +38 -1
- package/gs/bytes/buffer.gs.ts +48 -44
- package/gs/bytes/meta.json +8 -3
- package/gs/bytes/reader.gs.ts +20 -19
- package/gs/context/context.test.ts +41 -0
- package/gs/context/context.ts +22 -26
- package/gs/crypto/internal/fips140deps/byteorder/index.ts +1 -0
- package/gs/crypto/internal/fips140deps/godebug/index.ts +1 -0
- package/gs/crypto/rand/index.test.ts +32 -0
- package/gs/crypto/rand/index.ts +90 -0
- package/gs/crypto/rand/meta.json +5 -0
- package/gs/embed/index.ts +20 -0
- package/gs/embed/meta.json +5 -0
- package/gs/encoding/json/index.test.ts +79 -0
- package/gs/encoding/json/index.ts +210 -0
- package/gs/errors/errors.test.ts +82 -0
- package/gs/errors/errors.ts +104 -0
- package/gs/fmt/fmt.ts +56 -16
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +95 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +300 -2
- package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.test.ts +159 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.ts +1005 -0
- package/gs/github.com/aperturerobotics/starpc/srpc/index.ts +719 -0
- package/gs/github.com/aperturerobotics/starpc/srpc/meta.json +40 -0
- package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/meta.json +3 -1
- package/gs/github.com/klauspost/compress/internal/le/index.test.ts +36 -0
- package/gs/github.com/klauspost/compress/internal/le/index.ts +114 -0
- package/gs/go/internal/scannerhooks/index.test.ts +14 -0
- package/gs/go/internal/scannerhooks/index.ts +9 -0
- package/gs/go/scanner/index.test.ts +72 -0
- package/gs/go/scanner/index.ts +204 -0
- package/gs/go/token/index.test.ts +67 -0
- package/gs/go/token/index.ts +686 -0
- package/gs/internal/abi/index.test.ts +18 -0
- package/gs/internal/abi/index.ts +14 -0
- package/gs/internal/bytealg/index.test.ts +18 -0
- package/gs/internal/bytealg/index.ts +16 -0
- package/gs/internal/byteorder/index.test.ts +39 -0
- package/gs/internal/byteorder/index.ts +100 -27
- package/gs/internal/godebug/index.test.ts +16 -0
- package/gs/internal/godebug/index.ts +35 -0
- package/gs/io/fs/index.ts +1 -0
- package/gs/io/fs/meta.json +5 -0
- package/gs/io/fs/readlink.test.ts +43 -0
- package/gs/io/fs/readlink.ts +77 -0
- package/gs/io/fs/walk.test.ts +61 -0
- package/gs/io/fs/walk.ts +9 -9
- package/gs/io/io.ts +174 -31
- package/gs/io/meta.json +10 -2
- package/gs/maps/iter.ts +12 -6
- package/gs/maps/maps.ts +8 -6
- package/gs/math/bits/index.ts +103 -47
- package/gs/math/const.gs.test.ts +11 -5
- package/gs/math/const.gs.ts +5 -6
- package/gs/mime/index.ts +54 -0
- package/gs/net/http/httptest/index.ts +25 -0
- package/gs/net/http/index.test.ts +20 -0
- package/gs/net/http/index.ts +81 -0
- package/gs/os/dir_unix.gs.ts +2 -3
- package/gs/os/file_unix_js.test.ts +50 -0
- package/gs/os/meta.json +1 -2
- package/gs/os/tempfile.gs.test.ts +85 -0
- package/gs/os/tempfile.gs.ts +71 -11
- package/gs/os/types_js.gs.ts +11 -11
- package/gs/path/filepath/match.test.ts +31 -12
- package/gs/path/filepath/match.ts +178 -3
- package/gs/path/filepath/path.test.ts +25 -0
- package/gs/path/filepath/path.ts +159 -5
- package/gs/path/path.ts +20 -5
- package/gs/reflect/index.ts +2 -1
- package/gs/reflect/map.test.ts +19 -0
- package/gs/reflect/map.ts +4 -0
- package/gs/reflect/type.ts +298 -29
- package/gs/reflect/typefor.test.ts +75 -0
- package/gs/runtime/debug/index.test.ts +24 -0
- package/gs/runtime/debug/index.ts +8 -0
- package/gs/runtime/runtime.test.ts +19 -0
- package/gs/runtime/runtime.ts +98 -3
- package/gs/slices/slices.test.ts +94 -0
- package/gs/slices/slices.ts +245 -5
- package/gs/sort/meta.json +7 -0
- package/gs/sort/slice.gs.ts +16 -7
- package/gs/sort/sort.gs.ts +16 -13
- package/gs/strings/builder.ts +4 -3
- package/gs/sync/atomic/type.gs.ts +13 -14
- package/gs/sync/meta.json +3 -1
- package/gs/sync/sync.test.ts +36 -0
- package/gs/sync/sync.ts +39 -0
- package/gs/syscall/constants.ts +39 -24
- package/gs/syscall/errors.ts +10 -0
- package/gs/syscall/fs.ts +195 -0
- package/gs/syscall/js/index.ts +458 -0
- package/gs/syscall/js/meta.json +4 -0
- package/gs/syscall/net.test.ts +85 -0
- package/gs/syscall/types.ts +56 -0
- package/gs/testing/index.ts +1 -0
- package/gs/testing/meta.json +5 -0
- package/gs/testing/testing.test.ts +90 -0
- package/gs/testing/testing.ts +382 -0
- package/gs/time/time.test.ts +106 -0
- package/gs/time/time.ts +278 -57
- package/gs/unicode/unicode.test.ts +25 -0
- package/gs/unicode/unicode.ts +119 -9
- package/gs/unicode/utf8/utf8.test.ts +13 -0
- package/gs/unicode/utf8/utf8.ts +28 -16
- package/gs/unique/index.ts +91 -0
- package/package.json +14 -13
- package/compiler/analysis.go +0 -3475
- package/compiler/analysis_test.go +0 -338
- package/compiler/assignment.go +0 -580
- package/compiler/builtin_test.go +0 -92
- package/compiler/code-writer.go +0 -115
- package/compiler/compiler_test.go +0 -149
- package/compiler/composite-lit.go +0 -779
- package/compiler/config_test.go +0 -62
- package/compiler/constraint.go +0 -86
- package/compiler/decl.go +0 -801
- package/compiler/expr-call-async.go +0 -188
- package/compiler/expr-call-builtins.go +0 -208
- package/compiler/expr-call-helpers.go +0 -382
- package/compiler/expr-call-make.go +0 -318
- package/compiler/expr-call-type-conversion.go +0 -520
- package/compiler/expr-call.go +0 -413
- package/compiler/expr-selector.go +0 -343
- package/compiler/expr-star.go +0 -82
- package/compiler/expr-type.go +0 -442
- package/compiler/expr-value.go +0 -89
- package/compiler/expr.go +0 -773
- package/compiler/field.go +0 -183
- package/compiler/gs_dependencies_test.go +0 -298
- package/compiler/lit.go +0 -322
- package/compiler/output.go +0 -72
- package/compiler/primitive.go +0 -149
- package/compiler/protobuf.go +0 -697
- package/compiler/sanitize.go +0 -100
- package/compiler/spec-struct.go +0 -995
- package/compiler/spec-value.go +0 -540
- package/compiler/spec.go +0 -725
- package/compiler/stmt-assign.go +0 -664
- package/compiler/stmt-for.go +0 -266
- package/compiler/stmt-range.go +0 -475
- package/compiler/stmt-select.go +0 -262
- package/compiler/stmt-type-switch.go +0 -147
- package/compiler/stmt.go +0 -1308
- package/compiler/type-assert.go +0 -386
- package/compiler/type-info.go +0 -156
- package/compiler/type-utils.go +0 -207
- package/compiler/type.go +0 -892
package/compiler/stmt.go
DELETED
|
@@ -1,1308 +0,0 @@
|
|
|
1
|
-
package compiler
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"fmt"
|
|
5
|
-
"go/ast"
|
|
6
|
-
"go/token"
|
|
7
|
-
"go/types"
|
|
8
|
-
"strings"
|
|
9
|
-
|
|
10
|
-
"github.com/pkg/errors"
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
// WriteStmt is a central dispatcher function that translates a Go statement
|
|
14
|
-
// (`ast.Stmt`) into its TypeScript equivalent by calling the appropriate
|
|
15
|
-
// specialized `WriteStmt*` or `write*` method.
|
|
16
|
-
// It handles a wide variety of Go statements:
|
|
17
|
-
// - Block statements (`ast.BlockStmt`): `WriteStmtBlock`.
|
|
18
|
-
// - Assignment statements (`ast.AssignStmt`): `WriteStmtAssign`.
|
|
19
|
-
// - Return statements (`ast.ReturnStmt`): `WriteStmtReturn`.
|
|
20
|
-
// - Defer statements (`ast.DeferStmt`): `WriteStmtDefer`.
|
|
21
|
-
// - If statements (`ast.IfStmt`): `WriteStmtIf`.
|
|
22
|
-
// - Expression statements (`ast.ExprStmt`): `WriteStmtExpr`.
|
|
23
|
-
// - Declaration statements (`ast.DeclStmt`): `WriteStmtDecl`.
|
|
24
|
-
// - For statements (`ast.ForStmt`): `WriteStmtFor`.
|
|
25
|
-
// - Range statements (`ast.RangeStmt`): `WriteStmtRange`.
|
|
26
|
-
// - Switch statements (`ast.SwitchStmt`): `WriteStmtSwitch`.
|
|
27
|
-
// - Increment/decrement statements (`ast.IncDecStmt`): `WriteStmtIncDec`.
|
|
28
|
-
// - Send statements (`ast.SendStmt`): `WriteStmtSend`.
|
|
29
|
-
// - Go statements (`ast.GoStmt`): `WriteStmtGo`.
|
|
30
|
-
// - Select statements (`ast.SelectStmt`): `WriteStmtSelect`.
|
|
31
|
-
// - Branch statements (`ast.BranchStmt`): `WriteStmtBranch`.
|
|
32
|
-
// - Type switch statements (`ast.TypeSwitchStmt`): `WriteStmtTypeSwitch`.
|
|
33
|
-
// - Labeled statements (`ast.LabeledStmt`): `WriteStmtLabeled`.
|
|
34
|
-
//
|
|
35
|
-
// If an unknown statement type is encountered, it returns an error.
|
|
36
|
-
func (c *GoToTSCompiler) WriteStmt(a ast.Stmt) error {
|
|
37
|
-
switch exp := a.(type) {
|
|
38
|
-
case *ast.BlockStmt:
|
|
39
|
-
if err := c.WriteStmtBlock(exp, false); err != nil {
|
|
40
|
-
return fmt.Errorf("failed to write block statement: %w", err)
|
|
41
|
-
}
|
|
42
|
-
case *ast.AssignStmt:
|
|
43
|
-
// special case: if the left side of the assign has () we need a ; to prepend the line
|
|
44
|
-
// ;(myStruct!.value).MyInt = 5
|
|
45
|
-
// otherwise typescript assumes it is a function call
|
|
46
|
-
if len(exp.Lhs) == 1 {
|
|
47
|
-
if lhsSel, ok := exp.Lhs[0].(*ast.SelectorExpr); ok {
|
|
48
|
-
if _, ok := lhsSel.X.(*ast.ParenExpr); ok {
|
|
49
|
-
c.tsw.WriteLiterally(";")
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if err := c.WriteStmtAssign(exp); err != nil {
|
|
55
|
-
return fmt.Errorf("failed to write assignment statement: %w", err)
|
|
56
|
-
}
|
|
57
|
-
case *ast.ReturnStmt:
|
|
58
|
-
if err := c.WriteStmtReturn(exp); err != nil {
|
|
59
|
-
return fmt.Errorf("failed to write return statement: %w", err)
|
|
60
|
-
}
|
|
61
|
-
case *ast.DeferStmt:
|
|
62
|
-
if err := c.WriteStmtDefer(exp); err != nil {
|
|
63
|
-
return fmt.Errorf("failed to write defer statement: %w", err)
|
|
64
|
-
}
|
|
65
|
-
case *ast.IfStmt:
|
|
66
|
-
if err := c.WriteStmtIf(exp); err != nil {
|
|
67
|
-
return fmt.Errorf("failed to write if statement: %w", err)
|
|
68
|
-
}
|
|
69
|
-
case *ast.ExprStmt:
|
|
70
|
-
if err := c.WriteStmtExpr(exp); err != nil {
|
|
71
|
-
return fmt.Errorf("failed to write expression statement: %w", err)
|
|
72
|
-
}
|
|
73
|
-
case *ast.DeclStmt:
|
|
74
|
-
if err := c.WriteStmtDecl(exp); err != nil {
|
|
75
|
-
return fmt.Errorf("failed to write declaration statement: %w", err)
|
|
76
|
-
}
|
|
77
|
-
case *ast.ForStmt:
|
|
78
|
-
if err := c.WriteStmtFor(exp); err != nil {
|
|
79
|
-
return fmt.Errorf("failed to write for statement: %w", err)
|
|
80
|
-
}
|
|
81
|
-
case *ast.RangeStmt:
|
|
82
|
-
// Generate TS for for…range loops, log if something goes wrong
|
|
83
|
-
if err := c.WriteStmtRange(exp); err != nil {
|
|
84
|
-
return fmt.Errorf("failed to write range statement: %w", err)
|
|
85
|
-
}
|
|
86
|
-
case *ast.SwitchStmt:
|
|
87
|
-
if err := c.WriteStmtSwitch(exp); err != nil {
|
|
88
|
-
return fmt.Errorf("failed to write switch statement: %w", err)
|
|
89
|
-
}
|
|
90
|
-
case *ast.IncDecStmt:
|
|
91
|
-
if err := c.WriteStmtIncDec(exp); err != nil {
|
|
92
|
-
return fmt.Errorf("failed to write increment/decrement statement: %w", err)
|
|
93
|
-
}
|
|
94
|
-
case *ast.SendStmt:
|
|
95
|
-
if err := c.WriteStmtSend(exp); err != nil {
|
|
96
|
-
return fmt.Errorf("failed to write send statement: %w", err)
|
|
97
|
-
}
|
|
98
|
-
case *ast.GoStmt:
|
|
99
|
-
if err := c.WriteStmtGo(exp); err != nil {
|
|
100
|
-
return fmt.Errorf("failed to write go statement: %w", err)
|
|
101
|
-
}
|
|
102
|
-
case *ast.SelectStmt:
|
|
103
|
-
// Handle select statement
|
|
104
|
-
if err := c.WriteStmtSelect(exp); err != nil {
|
|
105
|
-
return fmt.Errorf("failed to write select statement: %w", err)
|
|
106
|
-
}
|
|
107
|
-
case *ast.BranchStmt:
|
|
108
|
-
if err := c.WriteStmtBranch(exp); err != nil {
|
|
109
|
-
return fmt.Errorf("failed to write branch statement: %w", err)
|
|
110
|
-
}
|
|
111
|
-
case *ast.TypeSwitchStmt:
|
|
112
|
-
if err := c.WriteStmtTypeSwitch(exp); err != nil {
|
|
113
|
-
return fmt.Errorf("failed to write type switch statement: %w", err)
|
|
114
|
-
}
|
|
115
|
-
case *ast.LabeledStmt:
|
|
116
|
-
if err := c.WriteStmtLabeled(exp); err != nil {
|
|
117
|
-
return fmt.Errorf("failed to write labeled statement: %w", err)
|
|
118
|
-
}
|
|
119
|
-
default:
|
|
120
|
-
return errors.Errorf("unknown statement: %#v\n", a)
|
|
121
|
-
}
|
|
122
|
-
return nil
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// WriteStmtDecl handles declaration statements (`ast.DeclStmt`),
|
|
126
|
-
// such as short variable declarations or type declarations within a statement list.
|
|
127
|
-
// It processes `ValueSpec`s and `TypeSpec`s within the declaration.
|
|
128
|
-
func (c *GoToTSCompiler) WriteStmtDecl(stmt *ast.DeclStmt) error {
|
|
129
|
-
// This typically contains a GenDecl
|
|
130
|
-
if genDecl, ok := stmt.Decl.(*ast.GenDecl); ok {
|
|
131
|
-
for _, spec := range genDecl.Specs {
|
|
132
|
-
// Value specs within a declaration statement
|
|
133
|
-
if valueSpec, ok := spec.(*ast.ValueSpec); ok {
|
|
134
|
-
if err := c.WriteValueSpec(valueSpec); err != nil {
|
|
135
|
-
return fmt.Errorf("failed to write value spec in declaration statement: %w", err)
|
|
136
|
-
}
|
|
137
|
-
} else if typeSpec, ok := spec.(*ast.TypeSpec); ok {
|
|
138
|
-
if err := c.WriteTypeSpec(typeSpec); err != nil {
|
|
139
|
-
return fmt.Errorf("failed to write type spec in declaration statement: %w", err)
|
|
140
|
-
}
|
|
141
|
-
} else {
|
|
142
|
-
return fmt.Errorf("unhandled spec in DeclStmt: %T", spec)
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
} else {
|
|
146
|
-
return errors.Errorf("unhandled declaration type in DeclStmt: %T", stmt.Decl)
|
|
147
|
-
}
|
|
148
|
-
return nil
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// WriteStmtIncDec handles increment and decrement statements (`ast.IncDecStmt`).
|
|
152
|
-
// It writes the expression followed by `++` or `--`.
|
|
153
|
-
func (c *GoToTSCompiler) WriteStmtIncDec(stmt *ast.IncDecStmt) error {
|
|
154
|
-
if err := c.WriteValueExpr(stmt.X); err != nil { // The expression (e.g., i)
|
|
155
|
-
return fmt.Errorf("failed to write increment/decrement expression: %w", err)
|
|
156
|
-
}
|
|
157
|
-
tokStr, ok := TokenToTs(stmt.Tok)
|
|
158
|
-
if !ok {
|
|
159
|
-
return errors.Errorf("unknown incdec token: %s", stmt.Tok.String())
|
|
160
|
-
}
|
|
161
|
-
c.tsw.WriteLiterally(tokStr) // The token (e.g., ++)
|
|
162
|
-
c.tsw.WriteLine("")
|
|
163
|
-
return nil
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// WriteStmtBranch handles branch statements (`ast.BranchStmt`), such as `break` and `continue`.
|
|
167
|
-
func (c *GoToTSCompiler) WriteStmtBranch(stmt *ast.BranchStmt) error {
|
|
168
|
-
switch stmt.Tok {
|
|
169
|
-
case token.BREAK:
|
|
170
|
-
if stmt.Label != nil {
|
|
171
|
-
c.tsw.WriteLinef("break %s", stmt.Label.Name)
|
|
172
|
-
} else {
|
|
173
|
-
c.tsw.WriteLine("break")
|
|
174
|
-
}
|
|
175
|
-
case token.CONTINUE:
|
|
176
|
-
if stmt.Label != nil {
|
|
177
|
-
c.tsw.WriteLinef("continue %s", stmt.Label.Name)
|
|
178
|
-
} else {
|
|
179
|
-
c.tsw.WriteLine("continue")
|
|
180
|
-
}
|
|
181
|
-
case token.GOTO:
|
|
182
|
-
// TypeScript doesn't support goto, but we can handle it by skipping it
|
|
183
|
-
// since the labeled statement restructuring should handle the control flow
|
|
184
|
-
c.tsw.WriteCommentLinef("goto %s // goto statement skipped", stmt.Label.Name)
|
|
185
|
-
case token.FALLTHROUGH:
|
|
186
|
-
// Fallthrough is handled in switch statements, should not appear elsewhere
|
|
187
|
-
c.tsw.WriteCommentLinef("fallthrough // fallthrough statement skipped")
|
|
188
|
-
default:
|
|
189
|
-
// This case should ideally not be reached if the Go parser is correct,
|
|
190
|
-
// as ast.BranchStmt only covers break, continue, goto, fallthrough.
|
|
191
|
-
c.tsw.WriteCommentLinef("unhandled branch statement token: %s", stmt.Tok.String())
|
|
192
|
-
}
|
|
193
|
-
return nil
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// WriteStmtGo translates a Go statement (`ast.GoStmt`) into its TypeScript equivalent.
|
|
197
|
-
// It handles `go func(){...}()`, `go namedFunc(args)`, and `go x.Method(args)`.
|
|
198
|
-
func (c *GoToTSCompiler) WriteStmtGo(exp *ast.GoStmt) error {
|
|
199
|
-
// Handle goroutine statement
|
|
200
|
-
// Translate 'go func() { ... }()' to 'queueMicrotask(() => { ... compiled body ... })'
|
|
201
|
-
callExpr := exp.Call
|
|
202
|
-
|
|
203
|
-
switch fun := callExpr.Fun.(type) {
|
|
204
|
-
case *ast.FuncLit:
|
|
205
|
-
// For function literals, we need to check if the function literal itself is async
|
|
206
|
-
// This happens during analysis in analysisVisitor.Visit for FuncLit nodes
|
|
207
|
-
isAsync := c.analysis.IsFuncLitAsync(fun)
|
|
208
|
-
if isAsync {
|
|
209
|
-
c.tsw.WriteLiterally("queueMicrotask(async () => ")
|
|
210
|
-
} else {
|
|
211
|
-
c.tsw.WriteLiterally("queueMicrotask(() => ")
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Compile the function literal's body directly
|
|
215
|
-
if err := c.WriteStmtBlock(fun.Body, true); err != nil {
|
|
216
|
-
return fmt.Errorf("failed to write goroutine function literal body: %w", err)
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
c.tsw.WriteLine(")") // Close the queueMicrotask statement
|
|
220
|
-
|
|
221
|
-
case *ast.Ident:
|
|
222
|
-
// Handle named functions: go namedFunc(args)
|
|
223
|
-
// Get the object for this function
|
|
224
|
-
obj := c.objectOfIdent(fun)
|
|
225
|
-
if obj == nil {
|
|
226
|
-
return errors.Errorf("could not find object for function: %s", fun.Name)
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Check if the function is async
|
|
230
|
-
isAsync := c.analysis.IsAsyncFunc(obj)
|
|
231
|
-
if isAsync {
|
|
232
|
-
c.tsw.WriteLiterally("queueMicrotask(async () => {")
|
|
233
|
-
} else {
|
|
234
|
-
c.tsw.WriteLiterally("queueMicrotask(() => {")
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
c.tsw.Indent(1)
|
|
238
|
-
c.tsw.WriteLine("")
|
|
239
|
-
|
|
240
|
-
// Write the function call, using await if the function is async
|
|
241
|
-
if isAsync {
|
|
242
|
-
c.tsw.WriteLiterally("await ")
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Write the function name
|
|
246
|
-
c.tsw.WriteLiterally(fun.Name)
|
|
247
|
-
|
|
248
|
-
// Write the function arguments
|
|
249
|
-
c.tsw.WriteLiterally("(")
|
|
250
|
-
for i, arg := range callExpr.Args {
|
|
251
|
-
if i != 0 {
|
|
252
|
-
c.tsw.WriteLiterally(", ")
|
|
253
|
-
}
|
|
254
|
-
if err := c.WriteValueExpr(arg); err != nil {
|
|
255
|
-
return fmt.Errorf("failed to write argument %d in goroutine function call: %w", i, err)
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
c.tsw.WriteLiterally(")")
|
|
259
|
-
c.tsw.WriteLine("")
|
|
260
|
-
|
|
261
|
-
c.tsw.Indent(-1)
|
|
262
|
-
c.tsw.WriteLine("})") // Close the queueMicrotask callback and the statement
|
|
263
|
-
case *ast.SelectorExpr:
|
|
264
|
-
// Handle selector expressions: go x.Method(args)
|
|
265
|
-
// Get the object for the selected method
|
|
266
|
-
obj := c.objectOfIdent(fun.Sel)
|
|
267
|
-
if obj == nil {
|
|
268
|
-
return errors.Errorf("could not find object for selected method: %s", fun.Sel.Name)
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// Check if the function is async
|
|
272
|
-
isAsync := c.analysis.IsAsyncFunc(obj)
|
|
273
|
-
if isAsync {
|
|
274
|
-
c.tsw.WriteLiterally("queueMicrotask(async () => {")
|
|
275
|
-
} else {
|
|
276
|
-
c.tsw.WriteLiterally("queueMicrotask(() => {")
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
c.tsw.Indent(1)
|
|
280
|
-
c.tsw.WriteLine("")
|
|
281
|
-
|
|
282
|
-
// Write the function call, using await if the function is async
|
|
283
|
-
if isAsync {
|
|
284
|
-
c.tsw.WriteLiterally("await ")
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// Write the selector expression (e.g., f.Bar)
|
|
288
|
-
// Note: callExpr.Fun is the *ast.SelectorExpr itself
|
|
289
|
-
// For method calls, we need to add null assertion since Go would panic on nil receiver
|
|
290
|
-
if selectorExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
|
|
291
|
-
if err := c.WriteValueExpr(selectorExpr.X); err != nil {
|
|
292
|
-
return fmt.Errorf("failed to write selector base expression in goroutine: %w", err)
|
|
293
|
-
}
|
|
294
|
-
// Add null assertion for method calls - Go would panic if receiver is nil
|
|
295
|
-
c.tsw.WriteLiterally("!.")
|
|
296
|
-
c.WriteIdent(selectorExpr.Sel, true)
|
|
297
|
-
} else {
|
|
298
|
-
if err := c.WriteValueExpr(callExpr.Fun); err != nil {
|
|
299
|
-
return fmt.Errorf("failed to write selector expression in goroutine: %w", err)
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// Write the function arguments
|
|
304
|
-
c.tsw.WriteLiterally("(")
|
|
305
|
-
for i, arg := range callExpr.Args {
|
|
306
|
-
if i != 0 {
|
|
307
|
-
c.tsw.WriteLiterally(", ")
|
|
308
|
-
}
|
|
309
|
-
if err := c.WriteValueExpr(arg); err != nil {
|
|
310
|
-
return fmt.Errorf("failed to write argument %d in goroutine selector function call: %w", i, err)
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
c.tsw.WriteLiterally(")")
|
|
314
|
-
c.tsw.WriteLine("")
|
|
315
|
-
|
|
316
|
-
c.tsw.Indent(-1)
|
|
317
|
-
c.tsw.WriteLine("})") // Close the queueMicrotask callback and the statement
|
|
318
|
-
case *ast.TypeAssertExpr:
|
|
319
|
-
// Handle type assertion expressions: go x.(func())()
|
|
320
|
-
// We assume this is always synchronous (no async function returned by type assertion)
|
|
321
|
-
c.tsw.WriteLiterally("queueMicrotask(() => {")
|
|
322
|
-
|
|
323
|
-
c.tsw.Indent(1)
|
|
324
|
-
c.tsw.WriteLine("")
|
|
325
|
-
|
|
326
|
-
// Write the type assertion call
|
|
327
|
-
if err := c.WriteTypeAssertExpr(fun); err != nil {
|
|
328
|
-
return fmt.Errorf("failed to write type assertion expression in goroutine: %w", err)
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// Add non-null assertion since mustTypeAssert throws on failure rather than returning null
|
|
332
|
-
c.tsw.WriteLiterally("!")
|
|
333
|
-
|
|
334
|
-
// Write the function arguments
|
|
335
|
-
c.tsw.WriteLiterally("(")
|
|
336
|
-
for i, arg := range callExpr.Args {
|
|
337
|
-
if i != 0 {
|
|
338
|
-
c.tsw.WriteLiterally(", ")
|
|
339
|
-
}
|
|
340
|
-
if err := c.WriteValueExpr(arg); err != nil {
|
|
341
|
-
return fmt.Errorf("failed to write argument %d in goroutine type assertion function call: %w", i, err)
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
c.tsw.WriteLiterally(")")
|
|
345
|
-
c.tsw.WriteLine("")
|
|
346
|
-
|
|
347
|
-
c.tsw.Indent(-1)
|
|
348
|
-
c.tsw.WriteLine("})") // Close the queueMicrotask callback and the statement
|
|
349
|
-
default:
|
|
350
|
-
return errors.Errorf("unhandled goroutine function type: %T", callExpr.Fun)
|
|
351
|
-
}
|
|
352
|
-
return nil
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// WriteStmtExpr translates a Go expression statement (`ast.ExprStmt`) into
|
|
356
|
-
// its TypeScript equivalent. An expression statement in Go is an expression
|
|
357
|
-
// evaluated for its side effects (e.g., a function call).
|
|
358
|
-
// - A special case is a simple channel receive used as a statement (`<-ch`). This
|
|
359
|
-
// is translated to `await ch_ts.receive();` (the value is discarded).
|
|
360
|
-
// - For other expression statements, the underlying expression `exp.X` is translated
|
|
361
|
-
// using `WriteValueExpr`.
|
|
362
|
-
// - It attempts to preserve inline comments associated with the expression statement
|
|
363
|
-
// or its underlying expression `exp.X`.
|
|
364
|
-
//
|
|
365
|
-
// The translated statement is terminated with a newline.
|
|
366
|
-
func (c *GoToTSCompiler) WriteStmtExpr(exp *ast.ExprStmt) error {
|
|
367
|
-
// Handle simple channel receive used as a statement (<-ch)
|
|
368
|
-
if unaryExpr, ok := exp.X.(*ast.UnaryExpr); ok && unaryExpr.Op == token.ARROW {
|
|
369
|
-
// Translate <-ch to await $.chanRecv(ch)
|
|
370
|
-
c.tsw.WriteLiterally("await $.chanRecv(")
|
|
371
|
-
if err := c.WriteValueExpr(unaryExpr.X); err != nil { // Channel expression
|
|
372
|
-
return fmt.Errorf("failed to write channel expression in receive statement: %w", err)
|
|
373
|
-
}
|
|
374
|
-
c.tsw.WriteLiterally(")") // Use chanRecv() as the value is discarded
|
|
375
|
-
c.tsw.WriteLine("")
|
|
376
|
-
return nil
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// Defensive semicolon: if the expression will start with '(' in TypeScript,
|
|
380
|
-
// prepend a semicolon to prevent JavaScript from treating the previous line
|
|
381
|
-
// as a function call. This happens when:
|
|
382
|
-
// 1. CallExpr where Fun itself will be parenthesized (e.g., (await fn())())
|
|
383
|
-
// 2. Array/slice literals starting with '['
|
|
384
|
-
if c.needsDefensiveSemicolon(exp.X) {
|
|
385
|
-
c.tsw.WriteLiterally(";")
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
// Handle other expression statements
|
|
389
|
-
if err := c.WriteValueExpr(exp.X); err != nil { // Expression statement evaluates a value
|
|
390
|
-
return err
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// Handle potential inline comment for ExprStmt
|
|
394
|
-
inlineCommentWritten := false
|
|
395
|
-
if c.pkg != nil && c.pkg.Fset != nil && exp.End().IsValid() {
|
|
396
|
-
if file := c.pkg.Fset.File(exp.End()); file != nil {
|
|
397
|
-
endLine := file.Line(exp.End())
|
|
398
|
-
// Check comments associated *directly* with the ExprStmt node
|
|
399
|
-
for _, cg := range c.analysis.Cmap[exp] {
|
|
400
|
-
if cg.Pos().IsValid() && file.Line(cg.Pos()) == endLine && cg.Pos() > exp.End() {
|
|
401
|
-
commentText := strings.TrimSpace(strings.TrimPrefix(cg.Text(), "//"))
|
|
402
|
-
c.tsw.WriteLiterally(" // " + commentText)
|
|
403
|
-
inlineCommentWritten = true
|
|
404
|
-
break
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
// Also check comments associated with the underlying expression X
|
|
408
|
-
// This might be necessary if the comment map links it to X instead of ExprStmt
|
|
409
|
-
if !inlineCommentWritten {
|
|
410
|
-
for _, cg := range c.analysis.Cmap[exp.X] {
|
|
411
|
-
if cg.Pos().IsValid() && file.Line(cg.Pos()) == endLine && cg.Pos() > exp.End() {
|
|
412
|
-
commentText := strings.TrimSpace(strings.TrimPrefix(cg.Text(), "//"))
|
|
413
|
-
c.tsw.WriteLiterally(" // " + commentText)
|
|
414
|
-
inlineCommentWritten = true //nolint:ineffassign
|
|
415
|
-
break
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// Add semicolon according to design doc (omit semicolons) - REMOVED semicolon
|
|
423
|
-
c.tsw.WriteLine("") // Finish with a newline
|
|
424
|
-
return nil
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// WriteStmtSend translates a Go channel send statement (`ast.SendStmt`),
|
|
428
|
-
// which has the form `ch <- value`, into its asynchronous TypeScript equivalent.
|
|
429
|
-
// The translation is `await ch_ts.send(value_ts)`.
|
|
430
|
-
// Both the channel expression (`exp.Chan`) and the value expression (`exp.Value`)
|
|
431
|
-
// are translated using `WriteValueExpr`. The `await` keyword is used because
|
|
432
|
-
// channel send operations are asynchronous in the TypeScript model.
|
|
433
|
-
// The statement is terminated with a newline.
|
|
434
|
-
func (c *GoToTSCompiler) WriteStmtSend(exp *ast.SendStmt) error {
|
|
435
|
-
// Translate ch <- value to await $.chanSend(ch, value)
|
|
436
|
-
c.tsw.WriteLiterally("await $.chanSend(")
|
|
437
|
-
if err := c.WriteValueExpr(exp.Chan); err != nil { // The channel expression
|
|
438
|
-
return fmt.Errorf("failed to write channel expression in send statement: %w", err)
|
|
439
|
-
}
|
|
440
|
-
c.tsw.WriteLiterally(", ")
|
|
441
|
-
if err := c.WriteValueExpr(exp.Value); err != nil { // The value expression
|
|
442
|
-
return fmt.Errorf("failed to write value expression in send statement: %w", err)
|
|
443
|
-
}
|
|
444
|
-
c.tsw.WriteLiterally(")")
|
|
445
|
-
c.tsw.WriteLine("") // Add newline after the statement
|
|
446
|
-
return nil
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
// WriteStmtIf translates a Go `if` statement (`ast.IfStmt`) into its
|
|
450
|
-
// TypeScript equivalent.
|
|
451
|
-
// - If the Go `if` has an initialization statement (`exp.Init`), it's wrapped
|
|
452
|
-
// in a TypeScript block `{...}` before the `if` keyword, and the initializer
|
|
453
|
-
// is translated within this block. This emulates Go's `if` statement scope.
|
|
454
|
-
// - The condition (`exp.Cond`) is translated using `WriteValueExpr` and placed
|
|
455
|
-
// within parentheses `(...)`.
|
|
456
|
-
// - The `if` body (`exp.Body`) is translated as a block statement using
|
|
457
|
-
// `WriteStmtBlock`. If `exp.Body` is nil, an empty block `{}` is written.
|
|
458
|
-
// - The `else` branch (`exp.Else`) is handled:
|
|
459
|
-
// - If `exp.Else` is a block statement (`ast.BlockStmt`), it's written as `else { ...body_ts... }`.
|
|
460
|
-
// - If `exp.Else` is another `if` statement (`ast.IfStmt`), it's written as `else if (...) ...`,
|
|
461
|
-
// recursively calling `WriteStmtIf`.
|
|
462
|
-
//
|
|
463
|
-
// The function aims to produce idiomatic TypeScript `if/else if/else` structures.
|
|
464
|
-
func (c *GoToTSCompiler) WriteStmtIf(exp *ast.IfStmt) error {
|
|
465
|
-
// Handle optional initialization statement
|
|
466
|
-
if exp.Init != nil {
|
|
467
|
-
shadowingInfo := c.analysis.GetShadowingInfo(exp)
|
|
468
|
-
|
|
469
|
-
// Write temp variables if shadowing detected
|
|
470
|
-
if shadowingInfo != nil {
|
|
471
|
-
c.writeShadowingTempVars(shadowingInfo)
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
c.tsw.WriteLine("{")
|
|
475
|
-
c.tsw.Indent(1)
|
|
476
|
-
|
|
477
|
-
// Write initialization with shadowing support if needed
|
|
478
|
-
if assignStmt, ok := exp.Init.(*ast.AssignStmt); ok && shadowingInfo != nil {
|
|
479
|
-
if err := c.writeAssignmentWithShadowing(assignStmt, shadowingInfo); err != nil {
|
|
480
|
-
return fmt.Errorf("failed to write shadowed assignment in if init: %w", err)
|
|
481
|
-
}
|
|
482
|
-
} else {
|
|
483
|
-
if err := c.WriteStmt(exp.Init); err != nil {
|
|
484
|
-
return fmt.Errorf("failed to write if initialization statement: %w", err)
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
// Write the if statement itself within the initialization block
|
|
489
|
-
c.tsw.WriteLiterally("if (")
|
|
490
|
-
if err := c.WriteValueExpr(exp.Cond); err != nil {
|
|
491
|
-
return err
|
|
492
|
-
}
|
|
493
|
-
c.tsw.WriteLiterally(") ")
|
|
494
|
-
|
|
495
|
-
if err := c.writeIfBody(exp); err != nil {
|
|
496
|
-
return err
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
c.tsw.Indent(-1)
|
|
500
|
-
c.tsw.WriteLine("}")
|
|
501
|
-
return nil
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
// No initialization statement, write if statement normally
|
|
505
|
-
c.tsw.WriteLiterally("if (")
|
|
506
|
-
if err := c.WriteValueExpr(exp.Cond); err != nil {
|
|
507
|
-
return err
|
|
508
|
-
}
|
|
509
|
-
c.tsw.WriteLiterally(") ")
|
|
510
|
-
|
|
511
|
-
return c.writeIfBody(exp)
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
// writeIfBody writes the if body and optional else clause, handling newline suppression.
|
|
515
|
-
func (c *GoToTSCompiler) writeIfBody(exp *ast.IfStmt) error {
|
|
516
|
-
hasElse := exp.Else != nil
|
|
517
|
-
if err := c.WriteStmtBlock(exp.Body, hasElse); err != nil {
|
|
518
|
-
return err
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
if hasElse {
|
|
522
|
-
c.tsw.WriteLiterally(" else ")
|
|
523
|
-
switch elseStmt := exp.Else.(type) {
|
|
524
|
-
case *ast.BlockStmt:
|
|
525
|
-
if err := c.WriteStmtBlock(elseStmt, false); err != nil {
|
|
526
|
-
return err
|
|
527
|
-
}
|
|
528
|
-
case *ast.IfStmt:
|
|
529
|
-
if err := c.WriteStmtIf(elseStmt); err != nil {
|
|
530
|
-
return err
|
|
531
|
-
}
|
|
532
|
-
default:
|
|
533
|
-
if err := c.WriteStmt(exp.Else); err != nil {
|
|
534
|
-
return err
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
return nil
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
// WriteStmtReturn translates a Go `return` statement (`ast.ReturnStmt`) into
|
|
543
|
-
// its TypeScript equivalent.
|
|
544
|
-
// - It writes the `return` keyword.
|
|
545
|
-
// - If there are multiple return values (`len(exp.Results) > 1`), the translated
|
|
546
|
-
// results are wrapped in a TypeScript array literal `[...]`.
|
|
547
|
-
// - Each result expression in `exp.Results` is translated using `WriteValueExpr`.
|
|
548
|
-
// - If there are no results, it simply writes `return`.
|
|
549
|
-
//
|
|
550
|
-
// The statement is terminated with a newline.
|
|
551
|
-
func (c *GoToTSCompiler) WriteStmtReturn(exp *ast.ReturnStmt) error {
|
|
552
|
-
c.tsw.WriteLiterally("return ")
|
|
553
|
-
|
|
554
|
-
// Check if it's a bare named return
|
|
555
|
-
nodeInfo := c.analysis.NodeData[exp]
|
|
556
|
-
if nodeInfo != nil && nodeInfo.IsBareReturn {
|
|
557
|
-
var namedReturns []string
|
|
558
|
-
if funcInfo := c.analysis.GetFunctionInfoFromContext(nodeInfo, c.pkg); funcInfo != nil {
|
|
559
|
-
namedReturns = funcInfo.NamedReturns
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
if len(namedReturns) == 1 {
|
|
563
|
-
// Single named return - don't wrap in array
|
|
564
|
-
c.tsw.WriteLiterally(c.sanitizeIdentifier(namedReturns[0]))
|
|
565
|
-
} else if len(namedReturns) > 1 {
|
|
566
|
-
// Multiple named returns - wrap in array
|
|
567
|
-
c.tsw.WriteLiterally("[")
|
|
568
|
-
for i, name := range namedReturns {
|
|
569
|
-
if i != 0 {
|
|
570
|
-
c.tsw.WriteLiterally(", ")
|
|
571
|
-
}
|
|
572
|
-
c.tsw.WriteLiterally(c.sanitizeIdentifier(name))
|
|
573
|
-
}
|
|
574
|
-
c.tsw.WriteLiterally("]")
|
|
575
|
-
}
|
|
576
|
-
} else {
|
|
577
|
-
// Handle explicit return values
|
|
578
|
-
if len(exp.Results) > 1 {
|
|
579
|
-
c.tsw.WriteLiterally("[")
|
|
580
|
-
}
|
|
581
|
-
for i, res := range exp.Results {
|
|
582
|
-
if i != 0 {
|
|
583
|
-
c.tsw.WriteLiterally(", ")
|
|
584
|
-
}
|
|
585
|
-
// Special handling for nil in generic function returns
|
|
586
|
-
if ident, isIdent := res.(*ast.Ident); isIdent && ident.Name == "nil" {
|
|
587
|
-
// Check if we're in a generic function and get the return type
|
|
588
|
-
if nodeInfo := c.analysis.NodeData[exp]; nodeInfo != nil && nodeInfo.EnclosingFuncDecl != nil {
|
|
589
|
-
funcDecl := nodeInfo.EnclosingFuncDecl
|
|
590
|
-
if funcDecl.Type.Results != nil && i < len(funcDecl.Type.Results.List) {
|
|
591
|
-
// Get the return type for this result position
|
|
592
|
-
resultField := funcDecl.Type.Results.List[i]
|
|
593
|
-
if resultType := c.pkg.TypesInfo.TypeOf(resultField.Type); resultType != nil {
|
|
594
|
-
if _, isTypeParam := resultType.(*types.TypeParam); isTypeParam {
|
|
595
|
-
// This is a generic type parameter, use type assertion with unknown intermediate
|
|
596
|
-
c.tsw.WriteLiterally("null as unknown as ")
|
|
597
|
-
c.WriteGoType(resultType, GoTypeContextFunctionReturn)
|
|
598
|
-
continue
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
// Special handling for primitive types that implement error interface
|
|
606
|
-
if c.writePrimitiveErrorWrapperIfNeeded(exp, res, i) {
|
|
607
|
-
continue
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
if err := c.WriteValueExpr(res); err != nil { // Return results are values
|
|
611
|
-
return err
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
if len(exp.Results) > 1 {
|
|
615
|
-
c.tsw.WriteLiterally("]")
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
c.tsw.WriteLine("")
|
|
619
|
-
return nil
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
// writePrimitiveErrorWrapperIfNeeded checks if a return value is a primitive type
|
|
623
|
-
// that implements the error interface, and if so, wraps it with $.wrapPrimitiveError.
|
|
624
|
-
// Returns true if the wrapper was written, false otherwise.
|
|
625
|
-
func (c *GoToTSCompiler) writePrimitiveErrorWrapperIfNeeded(retStmt *ast.ReturnStmt, res ast.Expr, resultIndex int) bool {
|
|
626
|
-
// Get the expected return type for this position
|
|
627
|
-
nodeInfo := c.analysis.NodeData[retStmt]
|
|
628
|
-
if nodeInfo == nil || nodeInfo.EnclosingFuncDecl == nil {
|
|
629
|
-
return false
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
funcDecl := nodeInfo.EnclosingFuncDecl
|
|
633
|
-
if funcDecl.Type.Results == nil {
|
|
634
|
-
return false
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
// Find the expected return type for this result index
|
|
638
|
-
var expectedType types.Type
|
|
639
|
-
resultIdx := 0
|
|
640
|
-
for _, field := range funcDecl.Type.Results.List {
|
|
641
|
-
count := len(field.Names)
|
|
642
|
-
if count == 0 {
|
|
643
|
-
count = 1
|
|
644
|
-
}
|
|
645
|
-
for j := 0; j < count; j++ {
|
|
646
|
-
if resultIdx == resultIndex {
|
|
647
|
-
expectedType = c.pkg.TypesInfo.TypeOf(field.Type)
|
|
648
|
-
break
|
|
649
|
-
}
|
|
650
|
-
resultIdx++
|
|
651
|
-
}
|
|
652
|
-
if expectedType != nil {
|
|
653
|
-
break
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
if expectedType == nil {
|
|
658
|
-
return false
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
// Check if the expected type is the error interface
|
|
662
|
-
if iface, ok := expectedType.Underlying().(*types.Interface); !ok || iface.String() != "interface{Error() string}" {
|
|
663
|
-
return false
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
// Get the actual type of the return expression
|
|
667
|
-
actualType := c.pkg.TypesInfo.TypeOf(res)
|
|
668
|
-
if actualType == nil {
|
|
669
|
-
return false
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
// Check if the actual type is a wrapper type (named type with basic underlying type)
|
|
673
|
-
if !c.isWrapperType(actualType) {
|
|
674
|
-
return false
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
// Check if the actual type has an Error() method
|
|
678
|
-
if !c.typeHasMethods(actualType, "Error") {
|
|
679
|
-
return false
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
// Get the qualified type name for the Error function
|
|
683
|
-
typeName := c.getQualifiedTypeName(actualType)
|
|
684
|
-
if typeName == "" {
|
|
685
|
-
return false
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
// Write: $.wrapPrimitiveError(value, TypeName_Error)
|
|
689
|
-
c.tsw.WriteLiterally("$.wrapPrimitiveError(")
|
|
690
|
-
if err := c.WriteValueExpr(res); err != nil {
|
|
691
|
-
return false
|
|
692
|
-
}
|
|
693
|
-
c.tsw.WriteLiterally(", ")
|
|
694
|
-
c.tsw.WriteLiterally(typeName)
|
|
695
|
-
c.tsw.WriteLiterally("_Error)")
|
|
696
|
-
|
|
697
|
-
return true
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
// WriteStmtBlock translates a Go block statement (`ast.BlockStmt`), typically
|
|
701
|
-
// `{ ...stmts... }`, into its TypeScript equivalent, carefully preserving
|
|
702
|
-
// comments and blank lines to maintain code readability and structure.
|
|
703
|
-
// - It writes an opening brace `{` and indents.
|
|
704
|
-
// - If the analysis (`c.analysis.NeedsDefer`) indicates that the block (or a
|
|
705
|
-
// function it's part of) contains `defer` statements, it injects a
|
|
706
|
-
// `using __defer = new $.DisposableStack();` (or `AsyncDisposableStack` if
|
|
707
|
-
// the context is async or contains async defers) at the beginning of the block.
|
|
708
|
-
// This `__defer` stack is used by `WriteStmtDefer` to register cleanup actions.
|
|
709
|
-
// - It iterates through the statements (`exp.List`) in the block:
|
|
710
|
-
// - Leading comments associated with each statement are written first, with
|
|
711
|
-
// blank lines preserved based on original source line numbers.
|
|
712
|
-
// - The statement itself is then translated using `WriteStmt`.
|
|
713
|
-
// - Inline comments (comments on the same line after a statement) are expected
|
|
714
|
-
// to be handled by the individual statement writers (e.g., `WriteStmtExpr`).
|
|
715
|
-
// - Trailing comments within the block (after the last statement but before the
|
|
716
|
-
// closing brace) are written.
|
|
717
|
-
// - Blank lines before the closing brace are preserved.
|
|
718
|
-
// - Finally, it unindents and writes the closing brace `}`.
|
|
719
|
-
//
|
|
720
|
-
// If `suppressNewline` is true, the final newline after the closing brace is omitted
|
|
721
|
-
// (used, for example, when an `if` block is followed by an `else`).
|
|
722
|
-
func (c *GoToTSCompiler) WriteStmtBlock(exp *ast.BlockStmt, suppressNewline bool) error {
|
|
723
|
-
if exp == nil {
|
|
724
|
-
c.tsw.WriteLiterally("{}")
|
|
725
|
-
if !suppressNewline {
|
|
726
|
-
c.tsw.WriteLine("")
|
|
727
|
-
}
|
|
728
|
-
return nil
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
// Opening brace
|
|
732
|
-
c.tsw.WriteLine("{")
|
|
733
|
-
c.tsw.Indent(1)
|
|
734
|
-
|
|
735
|
-
// Determine if there is any defer to an async function literal in this block
|
|
736
|
-
hasAsyncDefer := false
|
|
737
|
-
for _, stmt := range exp.List {
|
|
738
|
-
if deferStmt, ok := stmt.(*ast.DeferStmt); ok {
|
|
739
|
-
if deferStmt.Call == nil || deferStmt.Call.Fun == nil {
|
|
740
|
-
continue
|
|
741
|
-
}
|
|
742
|
-
if funcLit, ok := deferStmt.Call.Fun.(*ast.FuncLit); ok {
|
|
743
|
-
if c.analysis.IsFuncLitAsync(funcLit) {
|
|
744
|
-
hasAsyncDefer = true
|
|
745
|
-
break
|
|
746
|
-
}
|
|
747
|
-
} else {
|
|
748
|
-
// Check if the deferred call is to an async function
|
|
749
|
-
if c.isCallExprAsync(deferStmt.Call) {
|
|
750
|
-
hasAsyncDefer = true
|
|
751
|
-
break
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
// Add using statement if needed, considering async function or async defer
|
|
758
|
-
if c.analysis.NeedsDefer(exp) {
|
|
759
|
-
if c.analysis.IsInAsyncFunction(exp) || hasAsyncDefer {
|
|
760
|
-
c.tsw.WriteLine("await using __defer = new $.AsyncDisposableStack();")
|
|
761
|
-
} else {
|
|
762
|
-
c.tsw.WriteLine("using __defer = new $.DisposableStack();")
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
// Prepare line info
|
|
767
|
-
var file *token.File
|
|
768
|
-
if c.pkg != nil && c.pkg.Fset != nil && exp.Lbrace.IsValid() {
|
|
769
|
-
file = c.pkg.Fset.File(exp.Lbrace)
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
// writeBlank emits a single blank line if gap > 1
|
|
773
|
-
writeBlank := func(prev, curr int) {
|
|
774
|
-
if file != nil && prev > 0 && curr > prev+1 {
|
|
775
|
-
c.tsw.WriteLine("")
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
// Track last printed line, start at opening brace
|
|
780
|
-
lastLine := 0
|
|
781
|
-
if file != nil {
|
|
782
|
-
lastLine = file.Line(exp.Lbrace)
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
// 1. For each statement: write its leading comments, blank space, then the stmt
|
|
786
|
-
for _, stmt := range exp.List {
|
|
787
|
-
// Get statement's end line and position for inline comment check
|
|
788
|
-
stmtEndLine := 0
|
|
789
|
-
stmtEndPos := token.NoPos
|
|
790
|
-
if file != nil && stmt.End().IsValid() {
|
|
791
|
-
stmtEndLine = file.Line(stmt.End())
|
|
792
|
-
stmtEndPos = stmt.End()
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
// Process leading comments for stmt
|
|
796
|
-
comments := c.analysis.Cmap.Filter(stmt).Comments()
|
|
797
|
-
for _, cg := range comments {
|
|
798
|
-
// Check if this comment group is an inline comment for the current statement
|
|
799
|
-
isInlineComment := false
|
|
800
|
-
if file != nil && cg.Pos().IsValid() && stmtEndPos.IsValid() {
|
|
801
|
-
commentStartLine := file.Line(cg.Pos())
|
|
802
|
-
// Inline if starts on same line as stmt end AND starts after stmt end position
|
|
803
|
-
if commentStartLine == stmtEndLine && cg.Pos() > stmtEndPos {
|
|
804
|
-
isInlineComment = true
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
// If it's NOT an inline comment for this statement, write it here
|
|
809
|
-
if !isInlineComment {
|
|
810
|
-
start := 0
|
|
811
|
-
if file != nil && cg.Pos().IsValid() {
|
|
812
|
-
start = file.Line(cg.Pos())
|
|
813
|
-
}
|
|
814
|
-
writeBlank(lastLine, start)
|
|
815
|
-
c.WriteDoc(cg) // WriteDoc will handle the actual comment text
|
|
816
|
-
if file != nil && cg.End().IsValid() {
|
|
817
|
-
lastLine = file.Line(cg.End())
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
// If it IS an inline comment, skip it. The statement writer will handle it.
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
// the statement itself
|
|
824
|
-
stmtStart := 0
|
|
825
|
-
if file != nil && stmt.Pos().IsValid() {
|
|
826
|
-
stmtStart = file.Line(stmt.Pos())
|
|
827
|
-
}
|
|
828
|
-
writeBlank(lastLine, stmtStart)
|
|
829
|
-
// Call the specific statement writer (e.g., WriteStmtAssign).
|
|
830
|
-
// It is responsible for handling its own inline comment.
|
|
831
|
-
if err := c.WriteStmt(stmt); err != nil {
|
|
832
|
-
return fmt.Errorf("failed to write statement in block: %w", err)
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
if file != nil && stmt.End().IsValid() {
|
|
836
|
-
// Update lastLine based on the statement's end, *including* potential inline comment handled by WriteStmt*
|
|
837
|
-
lastLine = file.Line(stmt.End())
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
// 2. Trailing comments on the block (after last stmt, before closing brace)
|
|
842
|
-
trailing := c.analysis.Cmap.Filter(exp).Comments()
|
|
843
|
-
for _, cg := range trailing {
|
|
844
|
-
start := 0
|
|
845
|
-
if file != nil && cg.Pos().IsValid() {
|
|
846
|
-
start = file.Line(cg.Pos())
|
|
847
|
-
}
|
|
848
|
-
// only emit if it follows the last content
|
|
849
|
-
if start > lastLine {
|
|
850
|
-
writeBlank(lastLine, start)
|
|
851
|
-
c.WriteDoc(cg)
|
|
852
|
-
if file != nil && cg.End().IsValid() {
|
|
853
|
-
lastLine = file.Line(cg.End())
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
// 3. Blank lines before closing brace
|
|
859
|
-
closing := 0
|
|
860
|
-
if file != nil && exp.Rbrace.IsValid() {
|
|
861
|
-
closing = file.Line(exp.Rbrace)
|
|
862
|
-
}
|
|
863
|
-
writeBlank(lastLine, closing)
|
|
864
|
-
|
|
865
|
-
// Closing brace
|
|
866
|
-
c.tsw.Indent(-1)
|
|
867
|
-
c.tsw.WriteLiterally("}")
|
|
868
|
-
|
|
869
|
-
if !suppressNewline {
|
|
870
|
-
c.tsw.WriteLine("")
|
|
871
|
-
}
|
|
872
|
-
return nil
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
// WriteStmtSwitch translates a Go `switch` statement into its TypeScript equivalent.
|
|
876
|
-
// - If the Go switch has an initialization statement (`exp.Init`), it's wrapped
|
|
877
|
-
// in a TypeScript block `{...}` before the `switch` keyword, and the
|
|
878
|
-
// initializer is translated within this block. This emulates Go's switch scope.
|
|
879
|
-
// - The switch condition (`exp.Tag`):
|
|
880
|
-
// - If `exp.Tag` is present, it's translated using `WriteValueExpr`.
|
|
881
|
-
// - If `exp.Tag` is nil (a "tagless" switch, like `switch { case cond1: ... }`),
|
|
882
|
-
// it's translated as `switch (true)` in TypeScript.
|
|
883
|
-
// - Each case clause (`ast.CaseClause`) in `exp.Body.List` is translated using
|
|
884
|
-
// `WriteCaseClause`.
|
|
885
|
-
//
|
|
886
|
-
// The overall structure is `[optional_init_block] switch (condition_ts) { ...cases_ts... }`.
|
|
887
|
-
func (c *GoToTSCompiler) WriteStmtSwitch(exp *ast.SwitchStmt) error {
|
|
888
|
-
// Handle optional initialization statement
|
|
889
|
-
if exp.Init != nil {
|
|
890
|
-
c.tsw.WriteLiterally("{")
|
|
891
|
-
c.tsw.Indent(1)
|
|
892
|
-
if err := c.WriteStmt(exp.Init); err != nil {
|
|
893
|
-
return fmt.Errorf("failed to write switch initialization statement: %w", err)
|
|
894
|
-
}
|
|
895
|
-
defer func() {
|
|
896
|
-
c.tsw.Indent(-1)
|
|
897
|
-
c.tsw.WriteLiterally("}")
|
|
898
|
-
}()
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
c.tsw.WriteLiterally("switch (")
|
|
902
|
-
// Handle the switch tag (the expression being switched on)
|
|
903
|
-
if exp.Tag != nil {
|
|
904
|
-
if err := c.WriteValueExpr(exp.Tag); err != nil {
|
|
905
|
-
return fmt.Errorf("failed to write switch tag expression: %w", err)
|
|
906
|
-
}
|
|
907
|
-
} else {
|
|
908
|
-
c.tsw.WriteLiterally("true") // Write 'true' for switch without expression
|
|
909
|
-
}
|
|
910
|
-
c.tsw.WriteLiterally(") {")
|
|
911
|
-
c.tsw.WriteLine("")
|
|
912
|
-
c.tsw.Indent(1)
|
|
913
|
-
|
|
914
|
-
// Handle case clauses
|
|
915
|
-
for _, stmt := range exp.Body.List {
|
|
916
|
-
if caseClause, ok := stmt.(*ast.CaseClause); ok {
|
|
917
|
-
if err := c.WriteCaseClause(caseClause); err != nil {
|
|
918
|
-
return fmt.Errorf("failed to write case clause in switch statement: %w", err)
|
|
919
|
-
}
|
|
920
|
-
} else {
|
|
921
|
-
return fmt.Errorf("unhandled statement in switch body: %T", stmt)
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
c.tsw.Indent(-1)
|
|
926
|
-
c.tsw.WriteLine("}")
|
|
927
|
-
return nil
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
// WriteStmtDefer translates a Go `defer` statement into TypeScript code that
|
|
931
|
-
// utilizes a disposable stack (`$.DisposableStack` or `$.AsyncDisposableStack`).
|
|
932
|
-
// The Go `defer` semantics (LIFO execution at function exit) are emulated by
|
|
933
|
-
// registering a cleanup function with this stack.
|
|
934
|
-
// - `defer funcCall()` becomes `__defer.defer(() => { funcCall_ts(); });`.
|
|
935
|
-
// - `defer func(){ ...body... }()` (an immediately-invoked function literal, IIFL)
|
|
936
|
-
// has its body inlined: `__defer.defer(() => { ...body_ts... });`.
|
|
937
|
-
// - If the deferred call is to an async function or an async function literal
|
|
938
|
-
// (determined by `c.analysis.IsInAsyncFunctionMap`), the registered callback
|
|
939
|
-
// is marked `async`: `__defer.defer(async () => { ... });`.
|
|
940
|
-
//
|
|
941
|
-
// The `__defer` variable is assumed to be declared at the beginning of the
|
|
942
|
-
// function scope (see `WriteStmtBlock` or `WriteFuncDeclAsMethod`) using
|
|
943
|
-
// `await using __defer = new $.AsyncDisposableStack();` for async functions/contexts
|
|
944
|
-
// or `using __defer = new $.DisposableStack();` for sync contexts.
|
|
945
|
-
func (c *GoToTSCompiler) WriteStmtDefer(exp *ast.DeferStmt) error {
|
|
946
|
-
// Determine if the deferred call is to an async function literal using analysis
|
|
947
|
-
isAsyncDeferred := false
|
|
948
|
-
if funcLit, ok := exp.Call.Fun.(*ast.FuncLit); ok {
|
|
949
|
-
isAsyncDeferred = c.analysis.IsFuncLitAsync(funcLit)
|
|
950
|
-
} else {
|
|
951
|
-
// Check if the deferred call is to an async function
|
|
952
|
-
isAsyncDeferred = c.isCallExprAsync(exp.Call)
|
|
953
|
-
}
|
|
954
|
-
|
|
955
|
-
// Set async prefix based on pre-computed async status
|
|
956
|
-
asyncPrefix := ""
|
|
957
|
-
if isAsyncDeferred {
|
|
958
|
-
asyncPrefix = "async "
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
// Set stack variable based on whether we are in an async function
|
|
962
|
-
stackVar := "__defer"
|
|
963
|
-
c.tsw.WriteLiterallyf("%s.defer(%s() => {", stackVar, asyncPrefix)
|
|
964
|
-
c.tsw.Indent(1)
|
|
965
|
-
c.tsw.WriteLine("")
|
|
966
|
-
|
|
967
|
-
// Write the deferred call or inline the body when it's an immediately-invoked
|
|
968
|
-
// function literal (defer func(){ ... }()).
|
|
969
|
-
if funcLit, ok := exp.Call.Fun.(*ast.FuncLit); ok && len(exp.Call.Args) == 0 {
|
|
970
|
-
// Inline the function literal's body to avoid nested arrow invocation.
|
|
971
|
-
for _, stmt := range funcLit.Body.List {
|
|
972
|
-
if err := c.WriteStmt(stmt); err != nil {
|
|
973
|
-
return fmt.Errorf("failed to write statement in deferred function body: %w", err)
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
} else {
|
|
977
|
-
// Write the call expression as-is.
|
|
978
|
-
if err := c.WriteValueExpr(exp.Call); err != nil {
|
|
979
|
-
return fmt.Errorf("failed to write deferred call: %w", err)
|
|
980
|
-
}
|
|
981
|
-
c.tsw.WriteLine("")
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
c.tsw.Indent(-1)
|
|
985
|
-
c.tsw.WriteLine("});")
|
|
986
|
-
|
|
987
|
-
return nil
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
// WriteStmtLabeled handles labeled statements (ast.LabeledStmt), such as "label: statement".
|
|
991
|
-
// In TypeScript, labels cannot be used with variable declarations, so we need to handle this case specially.
|
|
992
|
-
func (c *GoToTSCompiler) WriteStmtLabeled(stmt *ast.LabeledStmt) error {
|
|
993
|
-
// Check if the labeled statement is a declaration statement or assignment with :=
|
|
994
|
-
needsBlock := false
|
|
995
|
-
if _, ok := stmt.Stmt.(*ast.DeclStmt); ok {
|
|
996
|
-
needsBlock = true
|
|
997
|
-
} else if assignStmt, ok := stmt.Stmt.(*ast.AssignStmt); ok && assignStmt.Tok == token.DEFINE {
|
|
998
|
-
// Assignment with := is also a declaration and needs special handling
|
|
999
|
-
needsBlock = true
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
if needsBlock {
|
|
1003
|
-
// For declaration statements and := assignments, we need to put the label on a separate line
|
|
1004
|
-
// because TypeScript doesn't allow labels with declarations
|
|
1005
|
-
c.tsw.WriteLiterally(stmt.Label.Name)
|
|
1006
|
-
c.tsw.WriteLine(": {")
|
|
1007
|
-
c.tsw.Indent(1)
|
|
1008
|
-
|
|
1009
|
-
// Write the statement without the label
|
|
1010
|
-
if err := c.WriteStmt(stmt.Stmt); err != nil {
|
|
1011
|
-
return fmt.Errorf("failed to write labeled declaration/assignment statement: %w", err)
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
c.tsw.Indent(-1)
|
|
1015
|
-
c.tsw.WriteLine("}")
|
|
1016
|
-
} else {
|
|
1017
|
-
// For non-declaration statements, write the label normally
|
|
1018
|
-
c.tsw.WriteLiterally(stmt.Label.Name)
|
|
1019
|
-
c.tsw.WriteLiterally(": ")
|
|
1020
|
-
|
|
1021
|
-
// Write the labeled statement
|
|
1022
|
-
if err := c.WriteStmt(stmt.Stmt); err != nil {
|
|
1023
|
-
return fmt.Errorf("failed to write labeled statement: %w", err)
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
return nil
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
// ============ Variable Shadowing Support ============
|
|
1031
|
-
|
|
1032
|
-
// writeShadowingTempVars creates temporary variables for shadowed variables
|
|
1033
|
-
func (c *GoToTSCompiler) writeShadowingTempVars(shadowingInfo *ShadowingInfo) {
|
|
1034
|
-
for varName, tempVarName := range shadowingInfo.TempVariables {
|
|
1035
|
-
c.tsw.WriteLiterally("const ")
|
|
1036
|
-
c.tsw.WriteLiterally(tempVarName)
|
|
1037
|
-
c.tsw.WriteLiterally(" = ")
|
|
1038
|
-
|
|
1039
|
-
// Check if this is a built-in function and handle it directly
|
|
1040
|
-
if c.isBuiltinFunction(varName) {
|
|
1041
|
-
c.tsw.WriteLiterally("$.")
|
|
1042
|
-
c.tsw.WriteLiterally(varName)
|
|
1043
|
-
} else {
|
|
1044
|
-
// Get the original object for this shadowed variable
|
|
1045
|
-
if originalObj, exists := shadowingInfo.ShadowedVariables[varName]; exists {
|
|
1046
|
-
// Create an identifier with the original name and use WriteValueExpr to properly resolve it
|
|
1047
|
-
originalIdent := &ast.Ident{Name: varName}
|
|
1048
|
-
// Set the identifier in the Uses map so WriteValueExpr can find the object
|
|
1049
|
-
c.pkg.TypesInfo.Uses[originalIdent] = originalObj
|
|
1050
|
-
c.WriteValueExpr(originalIdent)
|
|
1051
|
-
} else {
|
|
1052
|
-
// Fallback to literal name if no object found (shouldn't happen in normal cases)
|
|
1053
|
-
c.tsw.WriteLiterally(varName)
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
c.tsw.WriteLine("")
|
|
1057
|
-
}
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
// writeAssignmentWithShadowing writes an assignment statement that has variable shadowing.
|
|
1061
|
-
// Handles both regular assignments and type assertions, with optional temp variable creation.
|
|
1062
|
-
func (c *GoToTSCompiler) writeAssignmentWithShadowing(stmt *ast.AssignStmt, shadowingInfo *ShadowingInfo) error {
|
|
1063
|
-
// Check for type assertion special case
|
|
1064
|
-
if len(stmt.Rhs) == 1 && len(stmt.Lhs) == 2 {
|
|
1065
|
-
if typeAssert, ok := stmt.Rhs[0].(*ast.TypeAssertExpr); ok {
|
|
1066
|
-
return c.writeTypeAssertWithShadowing(stmt, typeAssert, shadowingInfo)
|
|
1067
|
-
}
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
// Regular assignment: write LHS declarations
|
|
1071
|
-
firstDecl := true
|
|
1072
|
-
for i, lhsExpr := range stmt.Lhs {
|
|
1073
|
-
if i > 0 {
|
|
1074
|
-
c.tsw.WriteLiterally(", ")
|
|
1075
|
-
}
|
|
1076
|
-
if ident, ok := lhsExpr.(*ast.Ident); ok {
|
|
1077
|
-
if ident.Name != "_" {
|
|
1078
|
-
if firstDecl {
|
|
1079
|
-
c.tsw.WriteLiterally("let ")
|
|
1080
|
-
firstDecl = false
|
|
1081
|
-
}
|
|
1082
|
-
c.WriteIdent(ident, false)
|
|
1083
|
-
} else {
|
|
1084
|
-
c.tsw.WriteLiterally("_")
|
|
1085
|
-
}
|
|
1086
|
-
} else {
|
|
1087
|
-
if err := c.WriteValueExpr(lhsExpr); err != nil {
|
|
1088
|
-
return err
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
// Write RHS with shadowed variable substitution
|
|
1094
|
-
c.tsw.WriteLiterally(" = ")
|
|
1095
|
-
for i, rhsExpr := range stmt.Rhs {
|
|
1096
|
-
if i > 0 {
|
|
1097
|
-
c.tsw.WriteLiterally(", ")
|
|
1098
|
-
}
|
|
1099
|
-
if err := c.substituteExprForShadowing(rhsExpr, shadowingInfo); err != nil {
|
|
1100
|
-
return err
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
c.tsw.WriteLine("")
|
|
1104
|
-
return nil
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
// writeTypeAssertWithShadowing writes a type assertion assignment (v, ok := x.(T)) with shadowing support
|
|
1108
|
-
func (c *GoToTSCompiler) writeTypeAssertWithShadowing(stmt *ast.AssignStmt, typeAssert *ast.TypeAssertExpr, shadowingInfo *ShadowingInfo) error {
|
|
1109
|
-
valueIdent, valueIsIdent := stmt.Lhs[0].(*ast.Ident)
|
|
1110
|
-
okIdent, okIsIdent := stmt.Lhs[1].(*ast.Ident)
|
|
1111
|
-
|
|
1112
|
-
if !valueIsIdent || !okIsIdent {
|
|
1113
|
-
return fmt.Errorf("type assertion LHS must be identifiers")
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
|
-
valueName := valueIdent.Name
|
|
1117
|
-
okName := okIdent.Name
|
|
1118
|
-
valueIsBlank := valueName == "_"
|
|
1119
|
-
okIsBlank := okName == "_"
|
|
1120
|
-
|
|
1121
|
-
if valueIsBlank && okIsBlank {
|
|
1122
|
-
// Both blank, evaluate RHS for side effects only
|
|
1123
|
-
if err := c.substituteExprForShadowing(typeAssert.X, shadowingInfo); err != nil {
|
|
1124
|
-
return err
|
|
1125
|
-
}
|
|
1126
|
-
c.tsw.WriteLine("")
|
|
1127
|
-
return nil
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
// Destructure into value and ok
|
|
1131
|
-
c.tsw.WriteLiterally("let { ")
|
|
1132
|
-
var parts []string
|
|
1133
|
-
if !valueIsBlank {
|
|
1134
|
-
parts = append(parts, "value: "+valueName)
|
|
1135
|
-
}
|
|
1136
|
-
if !okIsBlank {
|
|
1137
|
-
parts = append(parts, "ok: "+okName)
|
|
1138
|
-
}
|
|
1139
|
-
c.tsw.WriteLiterally(strings.Join(parts, ", "))
|
|
1140
|
-
c.tsw.WriteLiterally(" } = $.typeAssert<")
|
|
1141
|
-
c.WriteTypeExpr(typeAssert.Type)
|
|
1142
|
-
c.tsw.WriteLiterally(">(")
|
|
1143
|
-
if err := c.substituteExprForShadowing(typeAssert.X, shadowingInfo); err != nil {
|
|
1144
|
-
return err
|
|
1145
|
-
}
|
|
1146
|
-
c.tsw.WriteLiterally(", ")
|
|
1147
|
-
c.writeTypeDescription(typeAssert.Type)
|
|
1148
|
-
c.tsw.WriteLiterally(")")
|
|
1149
|
-
c.tsw.WriteLine("")
|
|
1150
|
-
return nil
|
|
1151
|
-
}
|
|
1152
|
-
|
|
1153
|
-
// substituteExprForShadowing writes an expression, replacing shadowed variables with temporary variables
|
|
1154
|
-
func (c *GoToTSCompiler) substituteExprForShadowing(expr ast.Expr, shadowingInfo *ShadowingInfo) error {
|
|
1155
|
-
switch e := expr.(type) {
|
|
1156
|
-
case *ast.Ident:
|
|
1157
|
-
// Check if this identifier is a shadowed variable
|
|
1158
|
-
if tempVar, isShadowed := shadowingInfo.TempVariables[e.Name]; isShadowed {
|
|
1159
|
-
// Use the temporary variable instead
|
|
1160
|
-
c.tsw.WriteLiterally(tempVar)
|
|
1161
|
-
} else {
|
|
1162
|
-
// Use the original identifier
|
|
1163
|
-
c.WriteIdent(e, true)
|
|
1164
|
-
}
|
|
1165
|
-
return nil
|
|
1166
|
-
|
|
1167
|
-
case *ast.CallExpr:
|
|
1168
|
-
// Handle function calls - replace identifiers in arguments with temp variables
|
|
1169
|
-
if err := c.substituteExprForShadowing(e.Fun, shadowingInfo); err != nil {
|
|
1170
|
-
return err
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
// Add non-null assertion for function calls (same logic as WriteCallExpr)
|
|
1174
|
-
c.addNonNullAssertion(e.Fun)
|
|
1175
|
-
|
|
1176
|
-
c.tsw.WriteLiterally("(")
|
|
1177
|
-
for i, arg := range e.Args {
|
|
1178
|
-
if i > 0 {
|
|
1179
|
-
c.tsw.WriteLiterally(", ")
|
|
1180
|
-
}
|
|
1181
|
-
if err := c.substituteExprForShadowing(arg, shadowingInfo); err != nil {
|
|
1182
|
-
return err
|
|
1183
|
-
}
|
|
1184
|
-
}
|
|
1185
|
-
c.tsw.WriteLiterally(")")
|
|
1186
|
-
return nil
|
|
1187
|
-
|
|
1188
|
-
case *ast.SelectorExpr:
|
|
1189
|
-
// Handle selector expressions (e.g., obj.Method)
|
|
1190
|
-
if err := c.substituteExprForShadowing(e.X, shadowingInfo); err != nil {
|
|
1191
|
-
return err
|
|
1192
|
-
}
|
|
1193
|
-
c.tsw.WriteLiterally(".")
|
|
1194
|
-
c.WriteIdent(e.Sel, true)
|
|
1195
|
-
return nil
|
|
1196
|
-
|
|
1197
|
-
case *ast.IndexExpr:
|
|
1198
|
-
// Handle index expressions (e.g., arr[i])
|
|
1199
|
-
if err := c.substituteExprForShadowing(e.X, shadowingInfo); err != nil {
|
|
1200
|
-
return err
|
|
1201
|
-
}
|
|
1202
|
-
c.tsw.WriteLiterally("[")
|
|
1203
|
-
if err := c.substituteExprForShadowing(e.Index, shadowingInfo); err != nil {
|
|
1204
|
-
return err
|
|
1205
|
-
}
|
|
1206
|
-
c.tsw.WriteLiterally("]")
|
|
1207
|
-
return nil
|
|
1208
|
-
|
|
1209
|
-
case *ast.UnaryExpr:
|
|
1210
|
-
// Handle unary expressions (e.g., &x, -x)
|
|
1211
|
-
c.tsw.WriteLiterally(e.Op.String())
|
|
1212
|
-
return c.substituteExprForShadowing(e.X, shadowingInfo)
|
|
1213
|
-
|
|
1214
|
-
case *ast.BinaryExpr:
|
|
1215
|
-
// Handle binary expressions (e.g., x + y)
|
|
1216
|
-
if err := c.substituteExprForShadowing(e.X, shadowingInfo); err != nil {
|
|
1217
|
-
return err
|
|
1218
|
-
}
|
|
1219
|
-
c.tsw.WriteLiterally(" ")
|
|
1220
|
-
c.tsw.WriteLiterally(e.Op.String())
|
|
1221
|
-
c.tsw.WriteLiterally(" ")
|
|
1222
|
-
return c.substituteExprForShadowing(e.Y, shadowingInfo)
|
|
1223
|
-
|
|
1224
|
-
case *ast.ParenExpr:
|
|
1225
|
-
// Handle parenthesized expressions
|
|
1226
|
-
c.tsw.WriteLiterally("(")
|
|
1227
|
-
if err := c.substituteExprForShadowing(e.X, shadowingInfo); err != nil {
|
|
1228
|
-
return err
|
|
1229
|
-
}
|
|
1230
|
-
c.tsw.WriteLiterally(")")
|
|
1231
|
-
return nil
|
|
1232
|
-
|
|
1233
|
-
default:
|
|
1234
|
-
// For other expression types, fall back to normal WriteValueExpr
|
|
1235
|
-
return c.WriteValueExpr(expr)
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
|
|
1239
|
-
// isBuiltinFunction checks if the given name is a Go built-in function
|
|
1240
|
-
func (c *GoToTSCompiler) isBuiltinFunction(name string) bool {
|
|
1241
|
-
return builtinFunctions[name]
|
|
1242
|
-
}
|
|
1243
|
-
|
|
1244
|
-
// needsDefensiveSemicolon determines if an expression will generate TypeScript
|
|
1245
|
-
// code starting with '(' or '[', which would require a defensive semicolon to
|
|
1246
|
-
// prevent JavaScript from treating the previous line as a function call.
|
|
1247
|
-
func (c *GoToTSCompiler) needsDefensiveSemicolon(expr ast.Expr) bool {
|
|
1248
|
-
switch e := expr.(type) {
|
|
1249
|
-
case *ast.CallExpr:
|
|
1250
|
-
// Check if the function being called will be parenthesized
|
|
1251
|
-
// This happens when Fun is itself a CallExpr, TypeAssertExpr, or other complex expression
|
|
1252
|
-
switch e.Fun.(type) {
|
|
1253
|
-
case *ast.CallExpr:
|
|
1254
|
-
// (fn())() - needs defensive semicolon
|
|
1255
|
-
return true
|
|
1256
|
-
case *ast.TypeAssertExpr:
|
|
1257
|
-
// (x.(T))() - needs defensive semicolon
|
|
1258
|
-
return true
|
|
1259
|
-
case *ast.IndexExpr:
|
|
1260
|
-
// Could generate (arr[i])() if indexed result is called
|
|
1261
|
-
// But typically doesn't need defensive semicolon as arr[i]() is fine
|
|
1262
|
-
return false
|
|
1263
|
-
case *ast.ParenExpr:
|
|
1264
|
-
// Already parenthesized - needs defensive semicolon
|
|
1265
|
-
return true
|
|
1266
|
-
}
|
|
1267
|
-
case *ast.CompositeLit:
|
|
1268
|
-
// Array/slice literals start with '['
|
|
1269
|
-
if _, isArray := e.Type.(*ast.ArrayType); isArray {
|
|
1270
|
-
return true
|
|
1271
|
-
}
|
|
1272
|
-
case *ast.ParenExpr:
|
|
1273
|
-
// Parenthesized expressions start with '('
|
|
1274
|
-
return true
|
|
1275
|
-
}
|
|
1276
|
-
return false
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
|
-
// isTerminatingStmt checks if a statement terminates control flow, meaning
|
|
1280
|
-
// code after it would be unreachable. This includes return, panic, continue,
|
|
1281
|
-
// break, goto, and fallthrough statements.
|
|
1282
|
-
func isTerminatingStmt(stmt ast.Stmt) bool {
|
|
1283
|
-
switch s := stmt.(type) {
|
|
1284
|
-
case *ast.ReturnStmt:
|
|
1285
|
-
return true
|
|
1286
|
-
case *ast.BranchStmt:
|
|
1287
|
-
// break, continue, goto, fallthrough all terminate the current block
|
|
1288
|
-
return true
|
|
1289
|
-
case *ast.ExprStmt:
|
|
1290
|
-
// Check for panic() calls
|
|
1291
|
-
if call, ok := s.X.(*ast.CallExpr); ok {
|
|
1292
|
-
if ident, ok := call.Fun.(*ast.Ident); ok {
|
|
1293
|
-
if ident.Name == "panic" {
|
|
1294
|
-
return true
|
|
1295
|
-
}
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
return false
|
|
1300
|
-
}
|
|
1301
|
-
|
|
1302
|
-
// endsWithTerminatingStmt checks if a statement list ends with a terminating statement.
|
|
1303
|
-
func endsWithTerminatingStmt(stmts []ast.Stmt) bool {
|
|
1304
|
-
if len(stmts) == 0 {
|
|
1305
|
-
return false
|
|
1306
|
-
}
|
|
1307
|
-
return isTerminatingStmt(stmts[len(stmts)-1])
|
|
1308
|
-
}
|