goscript 0.0.83 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -1
- package/cmd/goscript/cmd_compile.go +70 -69
- package/cmd/goscript/cmd_compile_test.go +79 -0
- package/cmd/goscript/main.go +10 -5
- package/compiler/compile-request.go +218 -0
- package/compiler/compiler.go +16 -1336
- package/compiler/compliance_test.go +196 -0
- package/compiler/config.go +6 -13
- package/compiler/diagnostic.go +70 -0
- package/compiler/index.test.ts +28 -28
- package/compiler/index.ts +40 -72
- package/compiler/lowered-program.go +132 -0
- package/compiler/lowering.go +3576 -0
- package/compiler/override-registry.go +422 -0
- package/compiler/override-registry_test.go +207 -0
- package/compiler/package-graph.go +231 -0
- package/compiler/package-graph_test.go +281 -0
- package/compiler/result.go +13 -0
- package/compiler/runtime-contract.go +279 -0
- package/compiler/runtime-contract_test.go +90 -0
- package/compiler/semantic-model-types.go +110 -0
- package/compiler/semantic-model.go +922 -0
- package/compiler/semantic-model_test.go +416 -0
- package/compiler/service.go +133 -0
- package/compiler/skeleton_test.go +1145 -0
- package/compiler/typescript-emitter.go +663 -0
- package/compiler/wasm/compile.go +2 -3
- package/compiler/wasm/compile_test.go +29 -0
- package/compiler/wasm_api.go +10 -159
- 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 +13 -0
- package/dist/gs/builtin/builtin.js +27 -7
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/channel.d.ts +3 -3
- package/dist/gs/builtin/channel.js.map +1 -1
- package/dist/gs/builtin/hostio.d.ts +86 -0
- package/dist/gs/builtin/hostio.js +266 -0
- package/dist/gs/builtin/hostio.js.map +1 -0
- 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/print.d.ts +8 -0
- package/dist/gs/builtin/print.js +111 -0
- package/dist/gs/builtin/print.js.map +1 -0
- package/dist/gs/builtin/slice.d.ts +1 -1
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/builtin/type.d.ts +11 -0
- package/dist/gs/builtin/type.js +55 -1
- package/dist/gs/builtin/type.js.map +1 -1
- 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.map +1 -1
- package/dist/gs/context/context.js.map +1 -1
- 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/encoding/json/index.d.ts +3 -0
- package/dist/gs/encoding/json/index.js +160 -0
- package/dist/gs/encoding/json/index.js.map +1 -0
- package/dist/gs/fmt/fmt.js +2 -22
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js.map +1 -1
- 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/scanner/index.d.ts +29 -0
- package/dist/gs/go/scanner/index.js +120 -0
- package/dist/gs/go/scanner/index.js.map +1 -0
- package/dist/gs/go/token/index.d.ts +31 -0
- package/dist/gs/go/token/index.js +82 -0
- package/dist/gs/go/token/index.js.map +1 -0
- package/dist/gs/internal/abi/index.js.map +1 -1
- package/dist/gs/io/fs/fs.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/stat.js.map +1 -1
- package/dist/gs/io/fs/sub.js.map +1 -1
- package/dist/gs/io/io.js.map +1 -1
- 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.d.ts +2 -51
- package/dist/gs/os/types_js.gs.js +67 -105
- 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.map +1 -1
- package/dist/gs/path/match.js.map +1 -1
- package/dist/gs/path/path.js.map +1 -1
- package/dist/gs/reflect/index.d.ts +2 -2
- package/dist/gs/reflect/index.js +1 -1
- package/dist/gs/reflect/index.js.map +1 -1
- package/dist/gs/reflect/map.js.map +1 -1
- package/dist/gs/reflect/type.d.ts +2 -1
- package/dist/gs/reflect/type.js +85 -14
- 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/runtime.js.map +1 -1
- 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.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.js.map +1 -1
- package/dist/gs/sync/atomic/value.gs.js.map +1 -1
- package/dist/gs/sync/sync.d.ts +1 -0
- package/dist/gs/sync/sync.js +12 -0
- package/dist/gs/sync/sync.js.map +1 -1
- package/dist/gs/time/time.js.map +1 -1
- package/dist/gs/unicode/unicode.js.map +1 -1
- package/go.mod +2 -2
- package/gs/builtin/builtin.ts +31 -6
- package/gs/builtin/hostio.test.ts +246 -0
- package/gs/builtin/hostio.ts +413 -0
- package/gs/builtin/index.ts +1 -0
- package/gs/builtin/print.test.ts +48 -0
- package/gs/builtin/print.ts +154 -0
- package/gs/builtin/runtime-contract.test.ts +230 -0
- package/gs/builtin/type.ts +84 -1
- 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/encoding/json/index.test.ts +65 -0
- package/gs/encoding/json/index.ts +186 -0
- package/gs/fmt/fmt.test.ts +41 -30
- package/gs/fmt/fmt.ts +2 -22
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +23 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +3 -1
- package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/meta.json +3 -1
- package/gs/go/scanner/index.test.ts +50 -0
- package/gs/go/scanner/index.ts +157 -0
- package/gs/go/token/index.test.ts +21 -0
- package/gs/go/token/index.ts +120 -0
- package/gs/os/file_unix_js.test.ts +103 -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 +74 -153
- package/gs/reflect/index.ts +1 -1
- package/gs/reflect/type.ts +106 -17
- package/gs/reflect/typefor.test.ts +75 -0
- package/gs/sync/sync.test.ts +24 -0
- package/gs/sync/sync.ts +12 -0
- package/package.json +13 -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/expr.go
DELETED
|
@@ -1,773 +0,0 @@
|
|
|
1
|
-
package compiler
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"fmt"
|
|
5
|
-
"go/ast"
|
|
6
|
-
"go/token"
|
|
7
|
-
"go/types"
|
|
8
|
-
|
|
9
|
-
"github.com/pkg/errors"
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
// WriteIndexExpr translates a Go index expression (a[b]) to its TypeScript equivalent.
|
|
13
|
-
func (c *GoToTSCompiler) WriteIndexExpr(exp *ast.IndexExpr) error {
|
|
14
|
-
// Check if this might be a generic function instantiation with a single type argument
|
|
15
|
-
// In this case, the Index should be a type expression, not a value expression
|
|
16
|
-
if tv, ok := c.pkg.TypesInfo.Types[exp.X]; ok {
|
|
17
|
-
// If X is a function type, this might be generic instantiation
|
|
18
|
-
if _, isFuncType := tv.Type.Underlying().(*types.Signature); isFuncType {
|
|
19
|
-
// Check if the index is a type expression (identifier that refers to a type)
|
|
20
|
-
if indexIdent, isIdent := exp.Index.(*ast.Ident); isIdent {
|
|
21
|
-
// Check if this identifier refers to a type
|
|
22
|
-
if obj := c.objectOfIdent(indexIdent); obj != nil {
|
|
23
|
-
if _, isTypeName := obj.(*types.TypeName); isTypeName {
|
|
24
|
-
// This is a generic function instantiation: f[T] -> f<T>
|
|
25
|
-
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
26
|
-
return err
|
|
27
|
-
}
|
|
28
|
-
c.tsw.WriteLiterally("<")
|
|
29
|
-
c.WriteTypeExpr(exp.Index)
|
|
30
|
-
c.tsw.WriteLiterally(">")
|
|
31
|
-
return nil
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Handle map access: use Map.get() instead of brackets for reading values
|
|
39
|
-
if tv, ok := c.pkg.TypesInfo.Types[exp.X]; ok {
|
|
40
|
-
underlyingType := tv.Type.Underlying()
|
|
41
|
-
// Check if it's a map type
|
|
42
|
-
if mapType, isMap := underlyingType.(*types.Map); isMap {
|
|
43
|
-
c.tsw.WriteLiterally("$.mapGet(")
|
|
44
|
-
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
45
|
-
return err
|
|
46
|
-
}
|
|
47
|
-
c.tsw.WriteLiterally(", ")
|
|
48
|
-
if err := c.WriteValueExpr(exp.Index); err != nil {
|
|
49
|
-
return err
|
|
50
|
-
}
|
|
51
|
-
c.tsw.WriteLiterally(", ")
|
|
52
|
-
|
|
53
|
-
// Generate the zero value as the default value for mapGet
|
|
54
|
-
c.WriteZeroValueForType(mapType.Elem())
|
|
55
|
-
c.tsw.WriteLiterally(")[0]") // Extract the value from the tuple
|
|
56
|
-
return nil
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Check if it's a string type
|
|
60
|
-
if c.isStringType(tv.Type) {
|
|
61
|
-
c.tsw.WriteLiterally("$.indexString(")
|
|
62
|
-
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
63
|
-
return err
|
|
64
|
-
}
|
|
65
|
-
c.tsw.WriteLiterally(", ")
|
|
66
|
-
if err := c.WriteValueExpr(exp.Index); err != nil {
|
|
67
|
-
return err
|
|
68
|
-
}
|
|
69
|
-
c.tsw.WriteLiterally(")")
|
|
70
|
-
return nil
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Check if it's a type parameter with a union constraint (e.g., string | []byte)
|
|
74
|
-
if typeParam, isTypeParam := tv.Type.(*types.TypeParam); isTypeParam {
|
|
75
|
-
// Check if the type parameter is constrained to slice types
|
|
76
|
-
constraint := typeParam.Constraint()
|
|
77
|
-
if constraint != nil {
|
|
78
|
-
underlying := constraint.Underlying()
|
|
79
|
-
if iface, isInterface := underlying.(*types.Interface); isInterface {
|
|
80
|
-
// Check if this is a map constraint (like ~map[K]V)
|
|
81
|
-
if hasMapConstraint(iface) {
|
|
82
|
-
// This is a map type parameter, use map access
|
|
83
|
-
c.tsw.WriteLiterally("$.mapGet(")
|
|
84
|
-
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
85
|
-
return err
|
|
86
|
-
}
|
|
87
|
-
c.tsw.WriteLiterally(", ")
|
|
88
|
-
if err := c.WriteValueExpr(exp.Index); err != nil {
|
|
89
|
-
return err
|
|
90
|
-
}
|
|
91
|
-
c.tsw.WriteLiterally(", ")
|
|
92
|
-
|
|
93
|
-
// Generate the zero value as the default value for mapGet
|
|
94
|
-
// For type parameters, we need to get the value type from the constraint
|
|
95
|
-
mapValueType := getMapValueTypeFromConstraint(iface)
|
|
96
|
-
if mapValueType != nil {
|
|
97
|
-
c.WriteZeroValueForType(mapValueType)
|
|
98
|
-
} else {
|
|
99
|
-
c.tsw.WriteLiterally("null")
|
|
100
|
-
}
|
|
101
|
-
c.tsw.WriteLiterally(")[0]") // Extract the value from the tuple
|
|
102
|
-
return nil
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Check if this is a mixed string/byte constraint (like string | []byte)
|
|
106
|
-
if hasMixedStringByteConstraint(iface) {
|
|
107
|
-
// For mixed constraints, use specialized function that returns number (byte value)
|
|
108
|
-
c.tsw.WriteLiterally("$.indexStringOrBytes(")
|
|
109
|
-
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
110
|
-
return err
|
|
111
|
-
}
|
|
112
|
-
c.tsw.WriteLiterally(", ")
|
|
113
|
-
if err := c.WriteValueExpr(exp.Index); err != nil {
|
|
114
|
-
return err
|
|
115
|
-
}
|
|
116
|
-
c.tsw.WriteLiterally(")")
|
|
117
|
-
return nil
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Check if the constraint includes only slice types (pure slice constraint)
|
|
121
|
-
if hasSliceConstraint(iface) {
|
|
122
|
-
// This is a pure slice type parameter, use regular slice indexing
|
|
123
|
-
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
124
|
-
return err
|
|
125
|
-
}
|
|
126
|
-
c.tsw.WriteLiterally("![") // non-null assertion
|
|
127
|
-
if err := c.WriteValueExpr(exp.Index); err != nil {
|
|
128
|
-
return err
|
|
129
|
-
}
|
|
130
|
-
c.tsw.WriteLiterally("]")
|
|
131
|
-
return nil
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// For other type parameters, use specialized function as fallback
|
|
137
|
-
// that returns number (byte value) for better TypeScript typing
|
|
138
|
-
c.tsw.WriteLiterally("$.indexStringOrBytes(")
|
|
139
|
-
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
140
|
-
return err
|
|
141
|
-
}
|
|
142
|
-
c.tsw.WriteLiterally(", ")
|
|
143
|
-
if err := c.WriteValueExpr(exp.Index); err != nil {
|
|
144
|
-
return err
|
|
145
|
-
}
|
|
146
|
-
c.tsw.WriteLiterally(")")
|
|
147
|
-
return nil
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Regular array/slice access: use brackets
|
|
152
|
-
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
153
|
-
return err
|
|
154
|
-
}
|
|
155
|
-
c.tsw.WriteLiterally("![") // non-null assertion
|
|
156
|
-
if err := c.WriteValueExpr(exp.Index); err != nil {
|
|
157
|
-
return err
|
|
158
|
-
}
|
|
159
|
-
c.tsw.WriteLiterally("]")
|
|
160
|
-
return nil
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// WriteIndexListExpr translates a Go generic function instantiation (f[T1, T2]) to its TypeScript equivalent (f<T1, T2>).
|
|
164
|
-
func (c *GoToTSCompiler) WriteIndexListExpr(exp *ast.IndexListExpr) error {
|
|
165
|
-
// Write the function expression
|
|
166
|
-
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
167
|
-
return err
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Write the type arguments using TypeScript syntax
|
|
171
|
-
c.tsw.WriteLiterally("<")
|
|
172
|
-
for i, typeArg := range exp.Indices {
|
|
173
|
-
if i > 0 {
|
|
174
|
-
c.tsw.WriteLiterally(", ")
|
|
175
|
-
}
|
|
176
|
-
c.WriteTypeExpr(typeArg)
|
|
177
|
-
}
|
|
178
|
-
c.tsw.WriteLiterally(">")
|
|
179
|
-
|
|
180
|
-
return nil
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// WriteTypeAssertExpr translates a Go type assertion expression (e.g., `x.(T)`)
|
|
184
|
-
// into a TypeScript call to `$.typeAssert<T_ts>(x_ts, 'TypeName').value`.
|
|
185
|
-
// The `$.typeAssert` runtime function handles the actual type check and panic
|
|
186
|
-
// if the assertion fails. The `.value` access is used because in an expression
|
|
187
|
-
// context, we expect the asserted value directly. The `TypeName` string is used
|
|
188
|
-
// by the runtime for error messages.
|
|
189
|
-
func (c *GoToTSCompiler) WriteTypeAssertExpr(exp *ast.TypeAssertExpr) error {
|
|
190
|
-
// Generate a call to $.typeAssert
|
|
191
|
-
c.tsw.WriteLiterally("$.mustTypeAssert<")
|
|
192
|
-
c.WriteTypeExpr(exp.Type) // Write the asserted type for the generic
|
|
193
|
-
c.tsw.WriteLiterally(">(")
|
|
194
|
-
if err := c.WriteValueExpr(exp.X); err != nil { // The interface expression
|
|
195
|
-
return fmt.Errorf("failed to write interface expression in type assertion expression: %w", err)
|
|
196
|
-
}
|
|
197
|
-
c.tsw.WriteLiterally(", ")
|
|
198
|
-
|
|
199
|
-
// Unwrap parenthesized expressions to handle cases like r.((<-chan T))
|
|
200
|
-
typeExpr := exp.Type
|
|
201
|
-
for {
|
|
202
|
-
if parenExpr, ok := typeExpr.(*ast.ParenExpr); ok {
|
|
203
|
-
typeExpr = parenExpr.X
|
|
204
|
-
} else {
|
|
205
|
-
break
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
c.writeTypeDescription(typeExpr)
|
|
210
|
-
|
|
211
|
-
c.tsw.WriteLiterally(")")
|
|
212
|
-
|
|
213
|
-
return nil
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// isPointerComparison checks if a binary expression `exp` involves comparing
|
|
217
|
-
// two pointer types. It uses `go/types` information to determine the types
|
|
218
|
-
// of the left (X) and right (Y) operands of the binary expression.
|
|
219
|
-
// Returns `true` if both operands are determined to be pointer types,
|
|
220
|
-
// `false` otherwise. This is used to apply specific comparison semantics
|
|
221
|
-
// for pointers (e.g., comparing the varRef objects directly).
|
|
222
|
-
func (c *GoToTSCompiler) isPointerComparison(exp *ast.BinaryExpr) bool {
|
|
223
|
-
leftType := c.pkg.TypesInfo.TypeOf(exp.X)
|
|
224
|
-
rightType := c.pkg.TypesInfo.TypeOf(exp.Y)
|
|
225
|
-
if leftType != nil && rightType != nil {
|
|
226
|
-
if _, leftIsPtr := leftType.(*types.Pointer); leftIsPtr {
|
|
227
|
-
if _, rightIsPtr := rightType.(*types.Pointer); rightIsPtr {
|
|
228
|
-
return true
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
return false
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// getTypeNameString returns a string representation of a Go type expression (`ast.Expr`).
|
|
236
|
-
// It handles simple identifiers (e.g., `MyType`) and selector expressions
|
|
237
|
-
// (e.g., `pkg.Type`). For more complex or unrecognized type expressions,
|
|
238
|
-
// it returns "unknown". This string is primarily used for runtime error messages,
|
|
239
|
-
// such as in type assertions.
|
|
240
|
-
func (c *GoToTSCompiler) getTypeNameString(typeExpr ast.Expr) string {
|
|
241
|
-
switch t := typeExpr.(type) {
|
|
242
|
-
case *ast.Ident:
|
|
243
|
-
return t.Name
|
|
244
|
-
case *ast.SelectorExpr:
|
|
245
|
-
// For imported types like pkg.Type
|
|
246
|
-
if ident, ok := t.X.(*ast.Ident); ok {
|
|
247
|
-
return fmt.Sprintf("%s.%s", ident.Name, t.Sel.Name)
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
// Default case, use a placeholder for complex types
|
|
251
|
-
return "unknown"
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// WriteBinaryExpr translates a Go binary expression (`ast.BinaryExpr`) into its
|
|
255
|
-
// TypeScript equivalent.
|
|
256
|
-
// It handles several cases:
|
|
257
|
-
// - Channel send (`ch <- val`): Becomes `await ch.send(val)`.
|
|
258
|
-
// - Nil comparison for pointers (`ptr == nil` or `ptr != nil`): Compares the
|
|
259
|
-
// pointer (which may be a varRef object or `null`) directly to `null` using
|
|
260
|
-
// the translated operator (`==` or `!=`).
|
|
261
|
-
// - Pointer comparison (non-nil, `ptr1 == ptr2` or `ptr1 != ptr2`): Compares
|
|
262
|
-
// the varRef objects directly using strict equality (`===` or `!==`).
|
|
263
|
-
// - Bitwise operations (`&`, `|`, `^`, `<<`, `>>`, `&^`): The expression is wrapped
|
|
264
|
-
// in parentheses `()` to ensure correct precedence in TypeScript, and operators
|
|
265
|
-
// are mapped (e.g., `&^` might need special handling or is mapped to a runtime helper).
|
|
266
|
-
// - Other binary operations (arithmetic, logical, comparison): Operands are
|
|
267
|
-
// translated using `WriteValueExpr`, and the operator is mapped to its TypeScript
|
|
268
|
-
// equivalent using `TokenToTs`.
|
|
269
|
-
//
|
|
270
|
-
// Unhandled operators result in a comment and a placeholder.
|
|
271
|
-
func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
|
|
272
|
-
// Handle special cases like channel send
|
|
273
|
-
if exp.Op == token.ARROW {
|
|
274
|
-
// Channel send: ch <- val becomes await $.chanSend(ch, val)
|
|
275
|
-
c.tsw.WriteLiterally("await $.chanSend(")
|
|
276
|
-
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
277
|
-
return fmt.Errorf("failed to write channel send target: %w", err)
|
|
278
|
-
}
|
|
279
|
-
c.tsw.WriteLiterally(", ")
|
|
280
|
-
if err := c.WriteValueExpr(exp.Y); err != nil {
|
|
281
|
-
return fmt.Errorf("failed to write channel send value: %w", err)
|
|
282
|
-
}
|
|
283
|
-
c.tsw.WriteLiterally(")")
|
|
284
|
-
return nil
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// Check if this is a nil comparison for a pointer
|
|
288
|
-
isNilComparison := false
|
|
289
|
-
var ptrExpr ast.Expr
|
|
290
|
-
if (exp.Op == token.EQL || exp.Op == token.NEQ) && c.pkg != nil && c.pkg.TypesInfo != nil {
|
|
291
|
-
if leftIdent, ok := exp.Y.(*ast.Ident); ok && leftIdent.Name == "nil" {
|
|
292
|
-
leftType := c.pkg.TypesInfo.TypeOf(exp.X)
|
|
293
|
-
if _, isPtr := leftType.(*types.Pointer); isPtr {
|
|
294
|
-
isNilComparison = true
|
|
295
|
-
ptrExpr = exp.X
|
|
296
|
-
}
|
|
297
|
-
} else if rightIdent, ok := exp.X.(*ast.Ident); ok && rightIdent.Name == "nil" {
|
|
298
|
-
rightType := c.pkg.TypesInfo.TypeOf(exp.Y)
|
|
299
|
-
if _, isPtr := rightType.(*types.Pointer); isPtr {
|
|
300
|
-
isNilComparison = true
|
|
301
|
-
ptrExpr = exp.Y
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
if isNilComparison {
|
|
307
|
-
// For nil comparisons, we need to decide whether to write .value or not
|
|
308
|
-
// If the pointer variable is varrefed, we need to access .value
|
|
309
|
-
if ident, ok := ptrExpr.(*ast.Ident); ok {
|
|
310
|
-
if obj := c.pkg.TypesInfo.ObjectOf(ident); obj != nil {
|
|
311
|
-
if c.analysis.NeedsVarRef(obj) {
|
|
312
|
-
// Variable is varrefed, so we need to access .value
|
|
313
|
-
c.WriteIdent(ident, true) // This will add .value
|
|
314
|
-
} else {
|
|
315
|
-
// Variable is not varrefed, write directly
|
|
316
|
-
c.WriteIdent(ident, false)
|
|
317
|
-
}
|
|
318
|
-
} else {
|
|
319
|
-
// No object info, write directly
|
|
320
|
-
c.WriteIdent(ident, false)
|
|
321
|
-
}
|
|
322
|
-
} else {
|
|
323
|
-
// For other expressions, use WriteValueExpr (but this might need review)
|
|
324
|
-
if err := c.WriteValueExpr(ptrExpr); err != nil {
|
|
325
|
-
return fmt.Errorf("failed to write pointer expression in nil comparison: %w", err)
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
c.tsw.WriteLiterally(" ")
|
|
329
|
-
tokStr, ok := TokenToTs(exp.Op)
|
|
330
|
-
if !ok {
|
|
331
|
-
return errors.Errorf("unhandled binary op: %s", exp.Op.String())
|
|
332
|
-
}
|
|
333
|
-
c.tsw.WriteLiterally(tokStr)
|
|
334
|
-
c.tsw.WriteLiterally(" null")
|
|
335
|
-
return nil
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// Check if this is a pointer comparison (non-nil)
|
|
339
|
-
// Compare the varRef objects directly using === or !==
|
|
340
|
-
if c.isPointerComparison(exp) {
|
|
341
|
-
c.tsw.WriteLiterally("(") // Wrap comparison
|
|
342
|
-
|
|
343
|
-
// For pointer comparisons, we need to handle variable varref status
|
|
344
|
-
// If a variable is varref'd, we need its .value to get the actual pointer value
|
|
345
|
-
|
|
346
|
-
// Check if operands are varref'd variables
|
|
347
|
-
var leftObj, rightObj types.Object
|
|
348
|
-
leftIsVarRef := false
|
|
349
|
-
rightIsVarRef := false
|
|
350
|
-
|
|
351
|
-
if leftIdent, ok := exp.X.(*ast.Ident); ok {
|
|
352
|
-
leftObj = c.pkg.TypesInfo.ObjectOf(leftIdent)
|
|
353
|
-
if leftObj != nil {
|
|
354
|
-
leftIsVarRef = c.analysis.NeedsVarRef(leftObj)
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
if rightIdent, ok := exp.Y.(*ast.Ident); ok {
|
|
359
|
-
rightObj = c.pkg.TypesInfo.ObjectOf(rightIdent)
|
|
360
|
-
if rightObj != nil {
|
|
361
|
-
rightIsVarRef = c.analysis.NeedsVarRef(rightObj)
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
// Write left operand
|
|
366
|
-
if leftIdent, ok := exp.X.(*ast.Ident); ok {
|
|
367
|
-
if leftIsVarRef {
|
|
368
|
-
// Variable is varref'd, access its .value to get the pointer
|
|
369
|
-
c.WriteIdent(leftIdent, true)
|
|
370
|
-
} else {
|
|
371
|
-
// Variable is not varref'd, use it directly
|
|
372
|
-
c.WriteIdent(leftIdent, false)
|
|
373
|
-
}
|
|
374
|
-
} else {
|
|
375
|
-
// For non-identifiers, use WriteValueExpr
|
|
376
|
-
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
377
|
-
return fmt.Errorf("failed to write binary expression left operand: %w", err)
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
c.tsw.WriteLiterally(" ")
|
|
382
|
-
// Use === for == and !== for !=
|
|
383
|
-
tokStr := ""
|
|
384
|
-
switch exp.Op {
|
|
385
|
-
case token.EQL:
|
|
386
|
-
tokStr = "==="
|
|
387
|
-
case token.NEQ:
|
|
388
|
-
tokStr = "!=="
|
|
389
|
-
default:
|
|
390
|
-
return errors.Errorf("unhandled pointer comparison op: %s", exp.Op.String())
|
|
391
|
-
}
|
|
392
|
-
c.tsw.WriteLiterally(tokStr)
|
|
393
|
-
c.tsw.WriteLiterally(" ")
|
|
394
|
-
|
|
395
|
-
// Write right operand
|
|
396
|
-
if rightIdent, ok := exp.Y.(*ast.Ident); ok {
|
|
397
|
-
if rightIsVarRef {
|
|
398
|
-
// Variable is varref'd, access its .value to get the pointer
|
|
399
|
-
c.WriteIdent(rightIdent, true)
|
|
400
|
-
} else {
|
|
401
|
-
// Variable is not varref'd, use it directly
|
|
402
|
-
c.WriteIdent(rightIdent, false)
|
|
403
|
-
}
|
|
404
|
-
} else {
|
|
405
|
-
// For non-identifiers, use WriteValueExpr
|
|
406
|
-
if err := c.WriteValueExpr(exp.Y); err != nil {
|
|
407
|
-
return fmt.Errorf("failed to write binary expression right operand: %w", err)
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
c.tsw.WriteLiterally(")") // Close wrap
|
|
412
|
-
return nil
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
// Check if the operator is a bitwise operator
|
|
416
|
-
isBitwise := false
|
|
417
|
-
switch exp.Op {
|
|
418
|
-
case token.AND, token.OR, token.XOR, token.SHL, token.SHR, token.AND_NOT:
|
|
419
|
-
isBitwise = true
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// Handle large bit shift expressions that would overflow in JavaScript
|
|
423
|
-
if exp.Op == token.SHL {
|
|
424
|
-
// Check if this is 1 << 63 pattern using constant evaluation
|
|
425
|
-
leftValue := c.evaluateConstantExpr(exp.X)
|
|
426
|
-
rightValue := c.evaluateConstantExpr(exp.Y)
|
|
427
|
-
|
|
428
|
-
if leftValue != nil && rightValue != nil {
|
|
429
|
-
if leftInt, leftOk := leftValue.(int); leftOk && leftInt == 1 {
|
|
430
|
-
if rightInt, rightOk := rightValue.(int); rightOk && rightInt == 63 {
|
|
431
|
-
// Replace 1 << 63 with Number.MAX_SAFE_INTEGER (9007199254740991)
|
|
432
|
-
// This is the largest integer that can be exactly represented in JavaScript
|
|
433
|
-
c.tsw.WriteLiterally("Number.MAX_SAFE_INTEGER")
|
|
434
|
-
return nil
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
if isBitwise {
|
|
441
|
-
c.tsw.WriteLiterally("(") // Add opening parenthesis for bitwise operations
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
// Check if this is a comparison that might trigger TypeScript's control flow narrowing.
|
|
445
|
-
// When a numeric field is compared to a literal, TypeScript may narrow the field type
|
|
446
|
-
// to that literal, causing subsequent comparisons to appear unintentional.
|
|
447
|
-
// We cast to number to widen the type and avoid these false positives.
|
|
448
|
-
needsNumberCast := false
|
|
449
|
-
if (exp.Op == token.EQL || exp.Op == token.NEQ) && c.pkg != nil && c.pkg.TypesInfo != nil {
|
|
450
|
-
// Check if left side is a selector expression (field access) and right is a literal or constant
|
|
451
|
-
_, leftIsSelector := exp.X.(*ast.SelectorExpr)
|
|
452
|
-
_, rightIsLiteral := exp.Y.(*ast.BasicLit)
|
|
453
|
-
// Also check if right side is an identifier referring to a constant
|
|
454
|
-
rightIsConstant := false
|
|
455
|
-
if rightIdent, ok := exp.Y.(*ast.Ident); ok {
|
|
456
|
-
if obj := c.pkg.TypesInfo.Uses[rightIdent]; obj != nil {
|
|
457
|
-
if _, isConst := obj.(*types.Const); isConst {
|
|
458
|
-
rightIsConstant = true
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
if leftIsSelector && (rightIsLiteral || rightIsConstant) {
|
|
463
|
-
leftType := c.pkg.TypesInfo.TypeOf(exp.X)
|
|
464
|
-
if leftType != nil {
|
|
465
|
-
if basic, ok := leftType.Underlying().(*types.Basic); ok {
|
|
466
|
-
// Check if it's a numeric type
|
|
467
|
-
if basic.Info()&types.IsNumeric != 0 {
|
|
468
|
-
needsNumberCast = true
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
// Check if this is integer division (Go's / operator truncates for integers)
|
|
476
|
-
isIntegerDivision := false
|
|
477
|
-
if exp.Op == token.QUO && c.pkg != nil && c.pkg.TypesInfo != nil {
|
|
478
|
-
leftType := c.pkg.TypesInfo.TypeOf(exp.X)
|
|
479
|
-
rightType := c.pkg.TypesInfo.TypeOf(exp.Y)
|
|
480
|
-
if leftType != nil && rightType != nil {
|
|
481
|
-
leftBasic, leftIsBasic := leftType.Underlying().(*types.Basic)
|
|
482
|
-
rightBasic, rightIsBasic := rightType.Underlying().(*types.Basic)
|
|
483
|
-
if leftIsBasic && rightIsBasic {
|
|
484
|
-
// Check if both are integer types (not floating point)
|
|
485
|
-
isIntegerDivision = (leftBasic.Info()&types.IsInteger != 0) && (rightBasic.Info()&types.IsInteger != 0)
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
if isIntegerDivision {
|
|
491
|
-
// Wrap integer division in Math.trunc() to match Go's integer division behavior
|
|
492
|
-
c.tsw.WriteLiterally("Math.trunc(")
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
if needsNumberCast {
|
|
496
|
-
c.tsw.WriteLiterally("Number(")
|
|
497
|
-
}
|
|
498
|
-
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
499
|
-
return fmt.Errorf("failed to write binary expression left operand: %w", err)
|
|
500
|
-
}
|
|
501
|
-
if needsNumberCast {
|
|
502
|
-
c.tsw.WriteLiterally(")")
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
c.tsw.WriteLiterally(" ")
|
|
506
|
-
tokStr, ok := TokenToTs(exp.Op)
|
|
507
|
-
if !ok {
|
|
508
|
-
return errors.Errorf("unhandled binary op: %s", exp.Op.String())
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
c.tsw.WriteLiterally(tokStr)
|
|
512
|
-
c.tsw.WriteLiterally(" ")
|
|
513
|
-
|
|
514
|
-
if err := c.WriteValueExpr(exp.Y); err != nil {
|
|
515
|
-
return fmt.Errorf("failed to write binary expression right operand: %w", err)
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
if isIntegerDivision {
|
|
519
|
-
c.tsw.WriteLiterally(")") // Close Math.trunc()
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
if isBitwise {
|
|
523
|
-
c.tsw.WriteLiterally(")") // Add closing parenthesis for bitwise operations
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
return nil
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
// WriteUnaryExpr translates a Go unary expression (`ast.UnaryExpr`) into its
|
|
530
|
-
// TypeScript equivalent.
|
|
531
|
-
// It handles several unary operations:
|
|
532
|
-
// - Channel receive (`<-ch`): Becomes `await ch.receive()`.
|
|
533
|
-
// - Address-of (`&var`):
|
|
534
|
-
// - If `var` is a varrefed variable (its address was taken), `&var` evaluates
|
|
535
|
-
// to the varRef itself (i.e., `varName` in TypeScript, which holds the varRef).
|
|
536
|
-
// - Otherwise (e.g., `&unvarrefedVar`, `&MyStruct{}`, `&FuncCall()`), it evaluates
|
|
537
|
-
// the operand `var`. The resulting TypeScript value (e.g., a new object instance)
|
|
538
|
-
// acts as the "pointer". VarRefing decisions for such pointers are handled at
|
|
539
|
-
// the assignment site.
|
|
540
|
-
// - Other unary operators (`+`, `-`, `!`, `^`): Mapped to their TypeScript
|
|
541
|
-
// equivalents (e.g., `+`, `-`, `!`, `~` for bitwise NOT). Parentheses are added
|
|
542
|
-
// around the operand if it's a binary or unary expression to maintain precedence.
|
|
543
|
-
//
|
|
544
|
-
// Unhandled operators result in a comment and an attempt to write the operator
|
|
545
|
-
// token directly. Postfix operators (`++`, `--`) are expected to be handled by
|
|
546
|
-
// their statement contexts (e.g., `IncDecStmt`).
|
|
547
|
-
func (c *GoToTSCompiler) WriteUnaryExpr(exp *ast.UnaryExpr) error {
|
|
548
|
-
if exp.Op == token.ARROW {
|
|
549
|
-
// Channel receive: <-ch becomes await $.chanRecv(ch)
|
|
550
|
-
c.tsw.WriteLiterally("await $.chanRecv(")
|
|
551
|
-
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
552
|
-
return fmt.Errorf("failed to write channel receive operand: %w", err)
|
|
553
|
-
}
|
|
554
|
-
c.tsw.WriteLiterally(")")
|
|
555
|
-
return nil
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
if exp.Op == token.AND { // Address-of operator (&)
|
|
559
|
-
// If the operand is an identifier for a variable that is varrefed,
|
|
560
|
-
// the result of & is the varRef itself.
|
|
561
|
-
if ident, ok := exp.X.(*ast.Ident); ok {
|
|
562
|
-
var obj types.Object
|
|
563
|
-
obj = c.pkg.TypesInfo.Uses[ident]
|
|
564
|
-
if obj == nil {
|
|
565
|
-
obj = c.pkg.TypesInfo.Defs[ident]
|
|
566
|
-
}
|
|
567
|
-
if obj != nil && c.analysis.NeedsVarRef(obj) {
|
|
568
|
-
// &varRefVar -> varRefVar (the variable reference itself)
|
|
569
|
-
c.tsw.WriteLiterally(c.sanitizeIdentifier(ident.Name)) // Write the identifier name (which holds the variable reference)
|
|
570
|
-
return nil
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
// Note: With inversion to markAsStructValue, we no longer mark &CompositeLit{}
|
|
575
|
-
// since we now mark the CompositeLit{} (struct values) instead of pointers
|
|
576
|
-
|
|
577
|
-
// Otherwise (&unvarrefedVar, &CompositeLit{}, &FuncCall(), etc.),
|
|
578
|
-
// the address-of operator in Go, when used to create a pointer,
|
|
579
|
-
// translates to simply evaluating the operand in TypeScript.
|
|
580
|
-
// The resulting value (e.g., a new object instance) acts as the "pointer".
|
|
581
|
-
// VarRefing decisions are handled at the assignment site based on the LHS variable.
|
|
582
|
-
|
|
583
|
-
// Set context flag to prevent marking composite literals as struct values
|
|
584
|
-
c.insideAddressOf = true
|
|
585
|
-
err := c.WriteValueExpr(exp.X)
|
|
586
|
-
c.insideAddressOf = false
|
|
587
|
-
|
|
588
|
-
if err != nil {
|
|
589
|
-
return fmt.Errorf("failed to write &-operand: %w", err)
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
return nil
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
// Handle other unary operators (+, -, !, ^)
|
|
596
|
-
tokStr, ok := TokenToTs(exp.Op)
|
|
597
|
-
if !ok {
|
|
598
|
-
return errors.Errorf("unhandled unary op: %s", exp.Op.String())
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
// Special case: In Go, ^ is bitwise NOT when used as unary operator
|
|
602
|
-
// In TypeScript, bitwise NOT is ~, not ^
|
|
603
|
-
if exp.Op == token.XOR {
|
|
604
|
-
tokStr = "~"
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
c.tsw.WriteLiterally(tokStr)
|
|
608
|
-
|
|
609
|
-
// Add space if operator is not postfix (e.g., !)
|
|
610
|
-
if exp.Op != token.INC && exp.Op != token.DEC {
|
|
611
|
-
// Check if operand needs parentheses (e.g., !(-a))
|
|
612
|
-
// Basic check: if operand is binary or unary, add parens
|
|
613
|
-
needsParens := false
|
|
614
|
-
switch exp.X.(type) {
|
|
615
|
-
case *ast.BinaryExpr, *ast.UnaryExpr:
|
|
616
|
-
needsParens = true
|
|
617
|
-
}
|
|
618
|
-
if needsParens {
|
|
619
|
-
c.tsw.WriteLiterally("(")
|
|
620
|
-
}
|
|
621
|
-
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
622
|
-
return fmt.Errorf("failed to write unary expression operand: %w", err)
|
|
623
|
-
}
|
|
624
|
-
if needsParens {
|
|
625
|
-
c.tsw.WriteLiterally(")")
|
|
626
|
-
}
|
|
627
|
-
} else {
|
|
628
|
-
// Postfix operators (++, --) - operand written first by caller (e.g., IncDecStmt)
|
|
629
|
-
// This function shouldn't be called directly for ++/-- in expression context in valid Go?
|
|
630
|
-
// If it is, write the operand.
|
|
631
|
-
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
632
|
-
return fmt.Errorf("failed to write unary expression operand for postfix op: %w", err)
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
return nil
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
// WriteSliceExpr translates a Go slice expression (e.g., `s[low:high:max]`) to its TypeScript equivalent.
|
|
640
|
-
// If `s` is a string and it's not a 3-index slice, it uses `s.substring(low, high)`.
|
|
641
|
-
// If `s` is `[]byte` (Uint8Array) and it's not a 3-index slice, it uses $.goSlice.
|
|
642
|
-
// Otherwise, it falls back to the `$.goSlice(s, low, high, max)` runtime helper.
|
|
643
|
-
func (c *GoToTSCompiler) WriteSliceExpr(exp *ast.SliceExpr) error {
|
|
644
|
-
// Check if the expression being sliced is a string
|
|
645
|
-
tv := c.pkg.TypesInfo.TypeOf(exp.X)
|
|
646
|
-
isString := false
|
|
647
|
-
isTypeParam := false
|
|
648
|
-
if tv != nil {
|
|
649
|
-
isString = c.isStringType(tv)
|
|
650
|
-
if _, isTP := tv.(*types.TypeParam); isTP {
|
|
651
|
-
isTypeParam = true
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
// Handle type parameters with union constraints (e.g., string | []byte)
|
|
656
|
-
if isTypeParam {
|
|
657
|
-
// For type parameters, we need to create a runtime helper that handles both string and []byte
|
|
658
|
-
c.tsw.WriteLiterally("$.sliceStringOrBytes(")
|
|
659
|
-
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
660
|
-
return err
|
|
661
|
-
}
|
|
662
|
-
c.tsw.WriteLiterally(", ")
|
|
663
|
-
if exp.Low != nil {
|
|
664
|
-
if err := c.WriteValueExpr(exp.Low); err != nil {
|
|
665
|
-
return err
|
|
666
|
-
}
|
|
667
|
-
} else {
|
|
668
|
-
c.tsw.WriteLiterally("undefined")
|
|
669
|
-
}
|
|
670
|
-
c.tsw.WriteLiterally(", ")
|
|
671
|
-
if exp.High != nil {
|
|
672
|
-
if err := c.WriteValueExpr(exp.High); err != nil {
|
|
673
|
-
return err
|
|
674
|
-
}
|
|
675
|
-
} else {
|
|
676
|
-
c.tsw.WriteLiterally("undefined")
|
|
677
|
-
}
|
|
678
|
-
if exp.Slice3 {
|
|
679
|
-
c.tsw.WriteLiterally(", ")
|
|
680
|
-
if exp.Max != nil {
|
|
681
|
-
if err := c.WriteValueExpr(exp.Max); err != nil {
|
|
682
|
-
return err
|
|
683
|
-
}
|
|
684
|
-
} else {
|
|
685
|
-
c.tsw.WriteLiterally("undefined")
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
c.tsw.WriteLiterally(")")
|
|
689
|
-
return nil
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
if isString && !exp.Slice3 {
|
|
693
|
-
// Use $.sliceString for byte-correct string slicing
|
|
694
|
-
c.tsw.WriteLiterally("$.sliceString(")
|
|
695
|
-
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
696
|
-
return err
|
|
697
|
-
}
|
|
698
|
-
c.tsw.WriteLiterally(", ")
|
|
699
|
-
if exp.Low != nil {
|
|
700
|
-
if err := c.WriteValueExpr(exp.Low); err != nil {
|
|
701
|
-
return err
|
|
702
|
-
}
|
|
703
|
-
} else {
|
|
704
|
-
// Go's default low for string[:high] is 0.
|
|
705
|
-
// $.sliceString can handle undefined for low as 0.
|
|
706
|
-
c.tsw.WriteLiterally("undefined")
|
|
707
|
-
}
|
|
708
|
-
c.tsw.WriteLiterally(", ")
|
|
709
|
-
if exp.High != nil {
|
|
710
|
-
if err := c.WriteValueExpr(exp.High); err != nil {
|
|
711
|
-
return err
|
|
712
|
-
}
|
|
713
|
-
} else {
|
|
714
|
-
// Go's default high for string[low:] means to the end.
|
|
715
|
-
// $.sliceString can handle undefined for high as end of string.
|
|
716
|
-
c.tsw.WriteLiterally("undefined")
|
|
717
|
-
}
|
|
718
|
-
c.tsw.WriteLiterally(")")
|
|
719
|
-
} else {
|
|
720
|
-
// Fallback to $.goSlice for actual slices (arrays) or 3-index string slices (which are rare and might need $.goSlice's complexity)
|
|
721
|
-
// Or if it's a string but has Slice3, it's not handled by simple substring.
|
|
722
|
-
c.tsw.WriteLiterally("$.goSlice(")
|
|
723
|
-
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
724
|
-
return err
|
|
725
|
-
}
|
|
726
|
-
c.tsw.WriteLiterally(", ")
|
|
727
|
-
if exp.Low != nil {
|
|
728
|
-
if err := c.WriteValueExpr(exp.Low); err != nil {
|
|
729
|
-
return err
|
|
730
|
-
}
|
|
731
|
-
} else {
|
|
732
|
-
c.tsw.WriteLiterally("undefined")
|
|
733
|
-
}
|
|
734
|
-
c.tsw.WriteLiterally(", ")
|
|
735
|
-
if exp.High != nil {
|
|
736
|
-
if err := c.WriteValueExpr(exp.High); err != nil {
|
|
737
|
-
return err
|
|
738
|
-
}
|
|
739
|
-
} else {
|
|
740
|
-
c.tsw.WriteLiterally("undefined")
|
|
741
|
-
}
|
|
742
|
-
if exp.Slice3 {
|
|
743
|
-
c.tsw.WriteLiterally(", ")
|
|
744
|
-
if exp.Max != nil {
|
|
745
|
-
if err := c.WriteValueExpr(exp.Max); err != nil {
|
|
746
|
-
return err
|
|
747
|
-
}
|
|
748
|
-
} else {
|
|
749
|
-
c.tsw.WriteLiterally("undefined")
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
c.tsw.WriteLiterally(")")
|
|
753
|
-
}
|
|
754
|
-
return nil
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
// WriteKeyValueExpr translates a Go key-value pair expression (`ast.KeyValueExpr`),
|
|
758
|
-
// typically found within composite literals (for structs, maps, or arrays with
|
|
759
|
-
// indexed elements), into its TypeScript object property equivalent: `key: value`.
|
|
760
|
-
// Both the key and the value expressions are recursively translated using
|
|
761
|
-
// `WriteValueExpr`. The original Go casing for keys is preserved.
|
|
762
|
-
// For example, `MyField: 123` in Go becomes `MyField: 123` in TypeScript.
|
|
763
|
-
func (c *GoToTSCompiler) WriteKeyValueExpr(exp *ast.KeyValueExpr) error {
|
|
764
|
-
// Keep original Go casing for keys
|
|
765
|
-
if err := c.WriteValueExpr(exp.Key); err != nil {
|
|
766
|
-
return fmt.Errorf("failed to write key-value expression key: %w", err)
|
|
767
|
-
}
|
|
768
|
-
c.tsw.WriteLiterally(": ")
|
|
769
|
-
if err := c.WriteValueExpr(exp.Value); err != nil {
|
|
770
|
-
return fmt.Errorf("failed to write key-value expression value: %w", err)
|
|
771
|
-
}
|
|
772
|
-
return nil
|
|
773
|
-
}
|