goscript 0.0.22 → 0.0.24
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 +1 -1
- package/cmd/goscript/cmd_compile.go +3 -3
- package/compiler/analysis.go +302 -182
- package/compiler/analysis_test.go +220 -0
- package/compiler/assignment.go +42 -43
- package/compiler/builtin_test.go +102 -0
- package/compiler/compiler.go +117 -29
- package/compiler/compiler_test.go +36 -8
- package/compiler/composite-lit.go +133 -53
- package/compiler/config.go +7 -3
- package/compiler/config_test.go +6 -33
- package/compiler/decl.go +36 -0
- package/compiler/expr-call.go +116 -60
- package/compiler/expr-selector.go +88 -43
- package/compiler/expr-star.go +57 -65
- package/compiler/expr-type.go +132 -5
- package/compiler/expr-value.go +8 -38
- package/compiler/expr.go +326 -30
- package/compiler/field.go +3 -3
- package/compiler/lit.go +34 -2
- package/compiler/primitive.go +19 -12
- package/compiler/spec-struct.go +140 -9
- package/compiler/spec-value.go +119 -41
- package/compiler/spec.go +21 -6
- package/compiler/stmt-assign.go +65 -3
- package/compiler/stmt-for.go +11 -0
- package/compiler/stmt-range.go +119 -11
- package/compiler/stmt-select.go +211 -0
- package/compiler/stmt-type-switch.go +147 -0
- package/compiler/stmt.go +175 -238
- package/compiler/type-assert.go +125 -379
- package/compiler/type.go +216 -129
- package/dist/gs/builtin/builtin.js +37 -0
- package/dist/gs/builtin/builtin.js.map +1 -0
- package/dist/gs/builtin/channel.js +471 -0
- package/dist/gs/builtin/channel.js.map +1 -0
- package/dist/gs/builtin/defer.js +54 -0
- package/dist/gs/builtin/defer.js.map +1 -0
- package/dist/gs/builtin/io.js +15 -0
- package/dist/gs/builtin/io.js.map +1 -0
- package/dist/gs/builtin/map.js +44 -0
- package/dist/gs/builtin/map.js.map +1 -0
- package/dist/gs/builtin/slice.js +799 -0
- package/dist/gs/builtin/slice.js.map +1 -0
- package/dist/gs/builtin/type.js +745 -0
- package/dist/gs/builtin/type.js.map +1 -0
- package/dist/gs/builtin/varRef.js +14 -0
- package/dist/gs/builtin/varRef.js.map +1 -0
- package/dist/gs/context/context.js +55 -0
- package/dist/gs/context/context.js.map +1 -0
- package/dist/gs/context/index.js +2 -0
- package/dist/gs/context/index.js.map +1 -0
- package/dist/gs/runtime/index.js +2 -0
- package/dist/gs/runtime/index.js.map +1 -0
- package/dist/gs/runtime/runtime.js +158 -0
- package/dist/gs/runtime/runtime.js.map +1 -0
- package/dist/gs/time/index.js +2 -0
- package/dist/gs/time/index.js.map +1 -0
- package/dist/gs/time/time.js +115 -0
- package/dist/gs/time/time.js.map +1 -0
- package/package.json +7 -6
- package/builtin/builtin.go +0 -11
- package/builtin/builtin.ts +0 -2379
- package/dist/builtin/builtin.d.ts +0 -513
- package/dist/builtin/builtin.js +0 -1686
- package/dist/builtin/builtin.js.map +0 -1
package/compiler/expr.go
CHANGED
|
@@ -11,10 +11,35 @@ import (
|
|
|
11
11
|
|
|
12
12
|
// WriteIndexExpr translates a Go index expression (a[b]) to its TypeScript equivalent.
|
|
13
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.pkg.TypesInfo.Uses[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
|
+
|
|
14
38
|
// Handle map access: use Map.get() instead of brackets for reading values
|
|
15
39
|
if tv, ok := c.pkg.TypesInfo.Types[exp.X]; ok {
|
|
40
|
+
underlyingType := tv.Type.Underlying()
|
|
16
41
|
// Check if it's a map type
|
|
17
|
-
if mapType, isMap :=
|
|
42
|
+
if mapType, isMap := underlyingType.(*types.Map); isMap {
|
|
18
43
|
c.tsw.WriteLiterally("$.mapGet(")
|
|
19
44
|
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
20
45
|
return err
|
|
@@ -30,6 +55,36 @@ func (c *GoToTSCompiler) WriteIndexExpr(exp *ast.IndexExpr) error {
|
|
|
30
55
|
c.tsw.WriteLiterally(")")
|
|
31
56
|
return nil
|
|
32
57
|
}
|
|
58
|
+
|
|
59
|
+
// Check if it's a string type
|
|
60
|
+
if basicType, isBasic := underlyingType.(*types.Basic); isBasic && (basicType.Info()&types.IsString) != 0 {
|
|
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 _, isTypeParam := tv.Type.(*types.TypeParam); isTypeParam {
|
|
75
|
+
// For type parameters with string | []byte constraint, use specialized function
|
|
76
|
+
// that returns number (byte value) for better TypeScript typing
|
|
77
|
+
c.tsw.WriteLiterally("$.indexStringOrBytes(")
|
|
78
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
79
|
+
return err
|
|
80
|
+
}
|
|
81
|
+
c.tsw.WriteLiterally(", ")
|
|
82
|
+
if err := c.WriteValueExpr(exp.Index); err != nil {
|
|
83
|
+
return err
|
|
84
|
+
}
|
|
85
|
+
c.tsw.WriteLiterally(")")
|
|
86
|
+
return nil
|
|
87
|
+
}
|
|
33
88
|
}
|
|
34
89
|
|
|
35
90
|
// Regular array/slice access: use brackets
|
|
@@ -44,6 +99,26 @@ func (c *GoToTSCompiler) WriteIndexExpr(exp *ast.IndexExpr) error {
|
|
|
44
99
|
return nil
|
|
45
100
|
}
|
|
46
101
|
|
|
102
|
+
// WriteIndexListExpr translates a Go generic function instantiation (f[T1, T2]) to its TypeScript equivalent (f<T1, T2>).
|
|
103
|
+
func (c *GoToTSCompiler) WriteIndexListExpr(exp *ast.IndexListExpr) error {
|
|
104
|
+
// Write the function expression
|
|
105
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
106
|
+
return err
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Write the type arguments using TypeScript syntax
|
|
110
|
+
c.tsw.WriteLiterally("<")
|
|
111
|
+
for i, typeArg := range exp.Indices {
|
|
112
|
+
if i > 0 {
|
|
113
|
+
c.tsw.WriteLiterally(", ")
|
|
114
|
+
}
|
|
115
|
+
c.WriteTypeExpr(typeArg)
|
|
116
|
+
}
|
|
117
|
+
c.tsw.WriteLiterally(">")
|
|
118
|
+
|
|
119
|
+
return nil
|
|
120
|
+
}
|
|
121
|
+
|
|
47
122
|
// WriteTypeAssertExpr translates a Go type assertion expression (e.g., `x.(T)`)
|
|
48
123
|
// into a TypeScript call to `$.typeAssert<T_ts>(x_ts, 'TypeName').value`.
|
|
49
124
|
// The `$.typeAssert` runtime function handles the actual type check and panic
|
|
@@ -52,7 +127,7 @@ func (c *GoToTSCompiler) WriteIndexExpr(exp *ast.IndexExpr) error {
|
|
|
52
127
|
// by the runtime for error messages.
|
|
53
128
|
func (c *GoToTSCompiler) WriteTypeAssertExpr(exp *ast.TypeAssertExpr) error {
|
|
54
129
|
// Generate a call to $.typeAssert
|
|
55
|
-
c.tsw.WriteLiterally("$.
|
|
130
|
+
c.tsw.WriteLiterally("$.mustTypeAssert<")
|
|
56
131
|
c.WriteTypeExpr(exp.Type) // Write the asserted type for the generic
|
|
57
132
|
c.tsw.WriteLiterally(">(")
|
|
58
133
|
if err := c.WriteValueExpr(exp.X); err != nil { // The interface expression
|
|
@@ -60,9 +135,6 @@ func (c *GoToTSCompiler) WriteTypeAssertExpr(exp *ast.TypeAssertExpr) error {
|
|
|
60
135
|
}
|
|
61
136
|
c.tsw.WriteLiterally(", ")
|
|
62
137
|
|
|
63
|
-
// Write the type description instead of just the type name
|
|
64
|
-
// This ensures we generate proper type info objects for all types
|
|
65
|
-
|
|
66
138
|
// Unwrap parenthesized expressions to handle cases like r.((<-chan T))
|
|
67
139
|
typeExpr := exp.Type
|
|
68
140
|
for {
|
|
@@ -75,7 +147,8 @@ func (c *GoToTSCompiler) WriteTypeAssertExpr(exp *ast.TypeAssertExpr) error {
|
|
|
75
147
|
|
|
76
148
|
c.writeTypeDescription(typeExpr)
|
|
77
149
|
|
|
78
|
-
c.tsw.WriteLiterally(")")
|
|
150
|
+
c.tsw.WriteLiterally(")")
|
|
151
|
+
|
|
79
152
|
return nil
|
|
80
153
|
}
|
|
81
154
|
|
|
@@ -84,7 +157,7 @@ func (c *GoToTSCompiler) WriteTypeAssertExpr(exp *ast.TypeAssertExpr) error {
|
|
|
84
157
|
// of the left (X) and right (Y) operands of the binary expression.
|
|
85
158
|
// Returns `true` if both operands are determined to be pointer types,
|
|
86
159
|
// `false` otherwise. This is used to apply specific comparison semantics
|
|
87
|
-
// for pointers (e.g., comparing the
|
|
160
|
+
// for pointers (e.g., comparing the varRef objects directly).
|
|
88
161
|
func (c *GoToTSCompiler) isPointerComparison(exp *ast.BinaryExpr) bool {
|
|
89
162
|
leftType := c.pkg.TypesInfo.TypeOf(exp.X)
|
|
90
163
|
rightType := c.pkg.TypesInfo.TypeOf(exp.Y)
|
|
@@ -122,10 +195,10 @@ func (c *GoToTSCompiler) getTypeNameString(typeExpr ast.Expr) string {
|
|
|
122
195
|
// It handles several cases:
|
|
123
196
|
// - Channel send (`ch <- val`): Becomes `await ch.send(val)`.
|
|
124
197
|
// - Nil comparison for pointers (`ptr == nil` or `ptr != nil`): Compares the
|
|
125
|
-
// pointer (which may be a
|
|
198
|
+
// pointer (which may be a varRef object or `null`) directly to `null` using
|
|
126
199
|
// the translated operator (`==` or `!=`).
|
|
127
200
|
// - Pointer comparison (non-nil, `ptr1 == ptr2` or `ptr1 != ptr2`): Compares
|
|
128
|
-
// the
|
|
201
|
+
// the varRef objects directly using strict equality (`===` or `!==`).
|
|
129
202
|
// - Bitwise operations (`&`, `|`, `^`, `<<`, `>>`, `&^`): The expression is wrapped
|
|
130
203
|
// in parentheses `()` to ensure correct precedence in TypeScript, and operators
|
|
131
204
|
// are mapped (e.g., `&^` might need special handling or is mapped to a runtime helper).
|
|
@@ -137,12 +210,12 @@ func (c *GoToTSCompiler) getTypeNameString(typeExpr ast.Expr) string {
|
|
|
137
210
|
func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
|
|
138
211
|
// Handle special cases like channel send
|
|
139
212
|
if exp.Op == token.ARROW {
|
|
140
|
-
// Channel send: ch <- val becomes await ch
|
|
141
|
-
c.tsw.WriteLiterally("await ")
|
|
213
|
+
// Channel send: ch <- val becomes await $.chanSend(ch, val)
|
|
214
|
+
c.tsw.WriteLiterally("await $.chanSend(")
|
|
142
215
|
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
143
216
|
return fmt.Errorf("failed to write channel send target: %w", err)
|
|
144
217
|
}
|
|
145
|
-
c.tsw.WriteLiterally("
|
|
218
|
+
c.tsw.WriteLiterally(", ")
|
|
146
219
|
if err := c.WriteValueExpr(exp.Y); err != nil {
|
|
147
220
|
return fmt.Errorf("failed to write channel send value: %w", err)
|
|
148
221
|
}
|
|
@@ -170,9 +243,26 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
|
|
|
170
243
|
}
|
|
171
244
|
|
|
172
245
|
if isNilComparison {
|
|
173
|
-
//
|
|
174
|
-
|
|
175
|
-
|
|
246
|
+
// For nil comparisons, we need to decide whether to write .value or not
|
|
247
|
+
// If the pointer variable is varrefed, we need to access .value
|
|
248
|
+
if ident, ok := ptrExpr.(*ast.Ident); ok {
|
|
249
|
+
if obj := c.pkg.TypesInfo.ObjectOf(ident); obj != nil {
|
|
250
|
+
if c.analysis.NeedsVarRef(obj) {
|
|
251
|
+
// Variable is varrefed, so we need to access .value
|
|
252
|
+
c.WriteIdent(ident, true) // This will add .value
|
|
253
|
+
} else {
|
|
254
|
+
// Variable is not varrefed, write directly
|
|
255
|
+
c.WriteIdent(ident, false)
|
|
256
|
+
}
|
|
257
|
+
} else {
|
|
258
|
+
// No object info, write directly
|
|
259
|
+
c.WriteIdent(ident, false)
|
|
260
|
+
}
|
|
261
|
+
} else {
|
|
262
|
+
// For other expressions, use WriteValueExpr (but this might need review)
|
|
263
|
+
if err := c.WriteValueExpr(ptrExpr); err != nil {
|
|
264
|
+
return fmt.Errorf("failed to write pointer expression in nil comparison: %w", err)
|
|
265
|
+
}
|
|
176
266
|
}
|
|
177
267
|
c.tsw.WriteLiterally(" ")
|
|
178
268
|
tokStr, ok := TokenToTs(exp.Op)
|
|
@@ -185,7 +275,7 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
|
|
|
185
275
|
}
|
|
186
276
|
|
|
187
277
|
// Check if this is a pointer comparison (non-nil)
|
|
188
|
-
// Compare the
|
|
278
|
+
// Compare the varRef objects directly using === or !==
|
|
189
279
|
if c.isPointerComparison(exp) {
|
|
190
280
|
c.tsw.WriteLiterally("(") // Wrap comparison
|
|
191
281
|
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
@@ -211,6 +301,50 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
|
|
|
211
301
|
return nil
|
|
212
302
|
}
|
|
213
303
|
|
|
304
|
+
// Check for Duration arithmetic operations (multiplication)
|
|
305
|
+
if exp.Op == token.MUL && c.pkg != nil && c.pkg.TypesInfo != nil {
|
|
306
|
+
leftType := c.pkg.TypesInfo.TypeOf(exp.X)
|
|
307
|
+
rightType := c.pkg.TypesInfo.TypeOf(exp.Y)
|
|
308
|
+
|
|
309
|
+
// Check if left operand is a Duration type (from time package)
|
|
310
|
+
if leftType != nil {
|
|
311
|
+
if namedType, ok := leftType.(*types.Named); ok {
|
|
312
|
+
if namedType.Obj().Pkg() != nil && namedType.Obj().Pkg().Path() == "time" && namedType.Obj().Name() == "Duration" {
|
|
313
|
+
// Duration * number -> Duration.multiply(duration, number)
|
|
314
|
+
c.tsw.WriteLiterally("$.multiplyDuration(")
|
|
315
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
316
|
+
return fmt.Errorf("failed to write Duration in multiplication: %w", err)
|
|
317
|
+
}
|
|
318
|
+
c.tsw.WriteLiterally(", ")
|
|
319
|
+
if err := c.WriteValueExpr(exp.Y); err != nil {
|
|
320
|
+
return fmt.Errorf("failed to write multiplier in Duration multiplication: %w", err)
|
|
321
|
+
}
|
|
322
|
+
c.tsw.WriteLiterally(")")
|
|
323
|
+
return nil
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Check if right operand is a Duration type (number * Duration)
|
|
329
|
+
if rightType != nil {
|
|
330
|
+
if namedType, ok := rightType.(*types.Named); ok {
|
|
331
|
+
if namedType.Obj().Pkg() != nil && namedType.Obj().Pkg().Path() == "time" && namedType.Obj().Name() == "Duration" {
|
|
332
|
+
// number * Duration -> Duration.multiply(duration, number)
|
|
333
|
+
c.tsw.WriteLiterally("$.multiplyDuration(")
|
|
334
|
+
if err := c.WriteValueExpr(exp.Y); err != nil {
|
|
335
|
+
return fmt.Errorf("failed to write Duration in multiplication: %w", err)
|
|
336
|
+
}
|
|
337
|
+
c.tsw.WriteLiterally(", ")
|
|
338
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
339
|
+
return fmt.Errorf("failed to write multiplier in Duration multiplication: %w", err)
|
|
340
|
+
}
|
|
341
|
+
c.tsw.WriteLiterally(")")
|
|
342
|
+
return nil
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
214
348
|
// Check if the operator is a bitwise operator
|
|
215
349
|
isBitwise := false
|
|
216
350
|
switch exp.Op {
|
|
@@ -218,6 +352,19 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
|
|
|
218
352
|
isBitwise = true
|
|
219
353
|
}
|
|
220
354
|
|
|
355
|
+
// Special handling for large bit shift expressions that would overflow in JavaScript
|
|
356
|
+
if exp.Op == token.SHL {
|
|
357
|
+
// Check if this is 1 << 63 pattern
|
|
358
|
+
if leftLit, leftIsLit := exp.X.(*ast.BasicLit); leftIsLit && leftLit.Value == "1" {
|
|
359
|
+
if rightLit, rightIsLit := exp.Y.(*ast.BasicLit); rightIsLit && rightLit.Value == "63" {
|
|
360
|
+
// Replace 1 << 63 with Number.MAX_SAFE_INTEGER (9007199254740991)
|
|
361
|
+
// This is the largest integer that can be exactly represented in JavaScript
|
|
362
|
+
c.tsw.WriteLiterally("Number.MAX_SAFE_INTEGER")
|
|
363
|
+
return nil
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
221
368
|
if isBitwise {
|
|
222
369
|
c.tsw.WriteLiterally("(") // Add opening parenthesis for bitwise operations
|
|
223
370
|
}
|
|
@@ -248,11 +395,11 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
|
|
|
248
395
|
// It handles several unary operations:
|
|
249
396
|
// - Channel receive (`<-ch`): Becomes `await ch.receive()`.
|
|
250
397
|
// - Address-of (`&var`):
|
|
251
|
-
// - If `var` is a
|
|
252
|
-
// to the
|
|
253
|
-
// - Otherwise (e.g., `&
|
|
398
|
+
// - If `var` is a varrefed variable (its address was taken), `&var` evaluates
|
|
399
|
+
// to the varRef itself (i.e., `varName` in TypeScript, which holds the varRef).
|
|
400
|
+
// - Otherwise (e.g., `&unvarrefedVar`, `&MyStruct{}`, `&FuncCall()`), it evaluates
|
|
254
401
|
// the operand `var`. The resulting TypeScript value (e.g., a new object instance)
|
|
255
|
-
// acts as the "pointer".
|
|
402
|
+
// acts as the "pointer". VarRefing decisions for such pointers are handled at
|
|
256
403
|
// the assignment site.
|
|
257
404
|
// - Other unary operators (`+`, `-`, `!`, `^`): Mapped to their TypeScript
|
|
258
405
|
// equivalents (e.g., `+`, `-`, `!`, `~` for bitwise NOT). Parentheses are added
|
|
@@ -263,36 +410,36 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
|
|
|
263
410
|
// their statement contexts (e.g., `IncDecStmt`).
|
|
264
411
|
func (c *GoToTSCompiler) WriteUnaryExpr(exp *ast.UnaryExpr) error {
|
|
265
412
|
if exp.Op == token.ARROW {
|
|
266
|
-
// Channel receive: <-ch becomes await ch
|
|
267
|
-
c.tsw.WriteLiterally("await ")
|
|
413
|
+
// Channel receive: <-ch becomes await $.chanRecv(ch)
|
|
414
|
+
c.tsw.WriteLiterally("await $.chanRecv(")
|
|
268
415
|
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
269
416
|
return fmt.Errorf("failed to write channel receive operand: %w", err)
|
|
270
417
|
}
|
|
271
|
-
c.tsw.WriteLiterally("
|
|
418
|
+
c.tsw.WriteLiterally(")")
|
|
272
419
|
return nil
|
|
273
420
|
}
|
|
274
421
|
|
|
275
422
|
if exp.Op == token.AND { // Address-of operator (&)
|
|
276
|
-
// If the operand is an identifier for a variable that is
|
|
277
|
-
// the result of & is the
|
|
423
|
+
// If the operand is an identifier for a variable that is varrefed,
|
|
424
|
+
// the result of & is the varRef itself.
|
|
278
425
|
if ident, ok := exp.X.(*ast.Ident); ok {
|
|
279
426
|
var obj types.Object
|
|
280
427
|
obj = c.pkg.TypesInfo.Uses[ident]
|
|
281
428
|
if obj == nil {
|
|
282
429
|
obj = c.pkg.TypesInfo.Defs[ident]
|
|
283
430
|
}
|
|
284
|
-
if obj != nil && c.analysis.
|
|
285
|
-
// &
|
|
286
|
-
c.tsw.WriteLiterally(ident.Name) // Write the identifier name (which holds the
|
|
431
|
+
if obj != nil && c.analysis.NeedsVarRef(obj) {
|
|
432
|
+
// &varRefVar -> varRefVar (the variable reference itself)
|
|
433
|
+
c.tsw.WriteLiterally(ident.Name) // Write the identifier name (which holds the variable reference)
|
|
287
434
|
return nil
|
|
288
435
|
}
|
|
289
436
|
}
|
|
290
437
|
|
|
291
|
-
// Otherwise (&
|
|
438
|
+
// Otherwise (&unvarrefedVar, &CompositeLit{}, &FuncCall(), etc.),
|
|
292
439
|
// the address-of operator in Go, when used to create a pointer,
|
|
293
440
|
// translates to simply evaluating the operand in TypeScript.
|
|
294
441
|
// The resulting value (e.g., a new object instance) acts as the "pointer".
|
|
295
|
-
//
|
|
442
|
+
// VarRefing decisions are handled at the assignment site based on the LHS variable.
|
|
296
443
|
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
297
444
|
return fmt.Errorf("failed to write &-operand: %w", err)
|
|
298
445
|
}
|
|
@@ -337,6 +484,155 @@ func (c *GoToTSCompiler) WriteUnaryExpr(exp *ast.UnaryExpr) error {
|
|
|
337
484
|
return nil
|
|
338
485
|
}
|
|
339
486
|
|
|
487
|
+
// WriteSliceExpr translates a Go slice expression (e.g., `s[low:high:max]`) to its TypeScript equivalent.
|
|
488
|
+
// If `s` is a string and it's not a 3-index slice, it uses `s.substring(low, high)`.
|
|
489
|
+
// If `s` is `[]byte` (Uint8Array) and it's not a 3-index slice, it uses `s.subarray(low, high)`.
|
|
490
|
+
// Otherwise, it falls back to the `$.goSlice(s, low, high, max)` runtime helper.
|
|
491
|
+
func (c *GoToTSCompiler) WriteSliceExpr(exp *ast.SliceExpr) error {
|
|
492
|
+
// Check if the expression being sliced is a string
|
|
493
|
+
tv := c.pkg.TypesInfo.TypeOf(exp.X)
|
|
494
|
+
isString := false
|
|
495
|
+
isByteSlice := false
|
|
496
|
+
isTypeParam := false
|
|
497
|
+
if tv != nil {
|
|
498
|
+
if basicType, isBasic := tv.Underlying().(*types.Basic); isBasic && (basicType.Info()&types.IsString) != 0 {
|
|
499
|
+
isString = true
|
|
500
|
+
}
|
|
501
|
+
if sliceType, isSlice := tv.Underlying().(*types.Slice); isSlice {
|
|
502
|
+
if basicElem, isBasic := sliceType.Elem().(*types.Basic); isBasic && basicElem.Kind() == types.Uint8 {
|
|
503
|
+
isByteSlice = true
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
if _, isTP := tv.(*types.TypeParam); isTP {
|
|
507
|
+
isTypeParam = true
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Handle type parameters with union constraints (e.g., string | []byte)
|
|
512
|
+
if isTypeParam {
|
|
513
|
+
// For type parameters, we need to create a runtime helper that handles both string and []byte
|
|
514
|
+
c.tsw.WriteLiterally("$.sliceStringOrBytes(")
|
|
515
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
516
|
+
return err
|
|
517
|
+
}
|
|
518
|
+
c.tsw.WriteLiterally(", ")
|
|
519
|
+
if exp.Low != nil {
|
|
520
|
+
if err := c.WriteValueExpr(exp.Low); err != nil {
|
|
521
|
+
return err
|
|
522
|
+
}
|
|
523
|
+
} else {
|
|
524
|
+
c.tsw.WriteLiterally("undefined")
|
|
525
|
+
}
|
|
526
|
+
c.tsw.WriteLiterally(", ")
|
|
527
|
+
if exp.High != nil {
|
|
528
|
+
if err := c.WriteValueExpr(exp.High); err != nil {
|
|
529
|
+
return err
|
|
530
|
+
}
|
|
531
|
+
} else {
|
|
532
|
+
c.tsw.WriteLiterally("undefined")
|
|
533
|
+
}
|
|
534
|
+
if exp.Slice3 {
|
|
535
|
+
c.tsw.WriteLiterally(", ")
|
|
536
|
+
if exp.Max != nil {
|
|
537
|
+
if err := c.WriteValueExpr(exp.Max); err != nil {
|
|
538
|
+
return err
|
|
539
|
+
}
|
|
540
|
+
} else {
|
|
541
|
+
c.tsw.WriteLiterally("undefined")
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
c.tsw.WriteLiterally(")")
|
|
545
|
+
return nil
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
if isString && !exp.Slice3 {
|
|
549
|
+
// Use $.sliceString for byte-correct string slicing
|
|
550
|
+
c.tsw.WriteLiterally("$.sliceString(")
|
|
551
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
552
|
+
return err
|
|
553
|
+
}
|
|
554
|
+
c.tsw.WriteLiterally(", ")
|
|
555
|
+
if exp.Low != nil {
|
|
556
|
+
if err := c.WriteValueExpr(exp.Low); err != nil {
|
|
557
|
+
return err
|
|
558
|
+
}
|
|
559
|
+
} else {
|
|
560
|
+
// Go's default low for string[:high] is 0.
|
|
561
|
+
// $.sliceString can handle undefined for low as 0.
|
|
562
|
+
c.tsw.WriteLiterally("undefined")
|
|
563
|
+
}
|
|
564
|
+
c.tsw.WriteLiterally(", ")
|
|
565
|
+
if exp.High != nil {
|
|
566
|
+
if err := c.WriteValueExpr(exp.High); err != nil {
|
|
567
|
+
return err
|
|
568
|
+
}
|
|
569
|
+
} else {
|
|
570
|
+
// Go's default high for string[low:] means to the end.
|
|
571
|
+
// $.sliceString can handle undefined for high as end of string.
|
|
572
|
+
c.tsw.WriteLiterally("undefined")
|
|
573
|
+
}
|
|
574
|
+
c.tsw.WriteLiterally(")")
|
|
575
|
+
} else if isByteSlice && !exp.Slice3 {
|
|
576
|
+
// Use s.subarray(low, high) for []byte slices
|
|
577
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
578
|
+
return err
|
|
579
|
+
}
|
|
580
|
+
c.tsw.WriteLiterally(".subarray(")
|
|
581
|
+
if exp.Low != nil {
|
|
582
|
+
if err := c.WriteValueExpr(exp.Low); err != nil {
|
|
583
|
+
return err
|
|
584
|
+
}
|
|
585
|
+
} else {
|
|
586
|
+
c.tsw.WriteLiterally("0") // Default low for subarray is 0
|
|
587
|
+
}
|
|
588
|
+
if exp.High != nil {
|
|
589
|
+
c.tsw.WriteLiterally(", ")
|
|
590
|
+
if err := c.WriteValueExpr(exp.High); err != nil {
|
|
591
|
+
return err
|
|
592
|
+
}
|
|
593
|
+
} else {
|
|
594
|
+
// If high is omitted, subarray goes to the end of the array.
|
|
595
|
+
// No need to write undefined or length, just close the parenthesis if low was the last arg.
|
|
596
|
+
}
|
|
597
|
+
c.tsw.WriteLiterally(")")
|
|
598
|
+
} else {
|
|
599
|
+
// Fallback to $.goSlice for actual slices (arrays) or 3-index string slices (which are rare and might need $.goSlice's complexity)
|
|
600
|
+
// Or if it's a string but has Slice3, it's not handled by simple substring.
|
|
601
|
+
c.tsw.WriteLiterally("$.goSlice(")
|
|
602
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
603
|
+
return err
|
|
604
|
+
}
|
|
605
|
+
c.tsw.WriteLiterally(", ")
|
|
606
|
+
if exp.Low != nil {
|
|
607
|
+
if err := c.WriteValueExpr(exp.Low); err != nil {
|
|
608
|
+
return err
|
|
609
|
+
}
|
|
610
|
+
} else {
|
|
611
|
+
c.tsw.WriteLiterally("undefined")
|
|
612
|
+
}
|
|
613
|
+
c.tsw.WriteLiterally(", ")
|
|
614
|
+
if exp.High != nil {
|
|
615
|
+
if err := c.WriteValueExpr(exp.High); err != nil {
|
|
616
|
+
return err
|
|
617
|
+
}
|
|
618
|
+
} else {
|
|
619
|
+
c.tsw.WriteLiterally("undefined")
|
|
620
|
+
}
|
|
621
|
+
if exp.Slice3 {
|
|
622
|
+
c.tsw.WriteLiterally(", ")
|
|
623
|
+
if exp.Max != nil {
|
|
624
|
+
if err := c.WriteValueExpr(exp.Max); err != nil {
|
|
625
|
+
return err
|
|
626
|
+
}
|
|
627
|
+
} else {
|
|
628
|
+
c.tsw.WriteLiterally("undefined")
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
c.tsw.WriteLiterally(")")
|
|
632
|
+
}
|
|
633
|
+
return nil
|
|
634
|
+
}
|
|
635
|
+
|
|
340
636
|
// WriteKeyValueExpr translates a Go key-value pair expression (`ast.KeyValueExpr`),
|
|
341
637
|
// typically found within composite literals (for structs, maps, or arrays with
|
|
342
638
|
// indexed elements), into its TypeScript object property equivalent: `key: value`.
|
package/compiler/field.go
CHANGED
|
@@ -50,7 +50,7 @@ func (c *GoToTSCompiler) WriteFieldList(a *ast.FieldList, isArguments bool) {
|
|
|
50
50
|
c.tsw.WriteLiterally(name.Name)
|
|
51
51
|
c.tsw.WriteLiterally(": ")
|
|
52
52
|
typ := c.pkg.TypesInfo.TypeOf(field.Type)
|
|
53
|
-
c.WriteGoType(typ)
|
|
53
|
+
c.WriteGoType(typ, GoTypeContextGeneral)
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
@@ -89,7 +89,7 @@ func (c *GoToTSCompiler) WriteFieldList(a *ast.FieldList, isArguments bool) {
|
|
|
89
89
|
c.tsw.WriteLiterally(name.Name)
|
|
90
90
|
c.tsw.WriteLiterally(": ")
|
|
91
91
|
typ := c.pkg.TypesInfo.TypeOf(field.Type)
|
|
92
|
-
c.WriteGoType(typ) // Use WriteGoType for parameter type
|
|
92
|
+
c.WriteGoType(typ, GoTypeContextGeneral) // Use WriteGoType for parameter type
|
|
93
93
|
}
|
|
94
94
|
} else {
|
|
95
95
|
// For struct fields and other non-argument fields
|
|
@@ -155,7 +155,7 @@ func (c *GoToTSCompiler) WriteField(field *ast.Field, isArguments bool) {
|
|
|
155
155
|
// write type for struct fields (not arguments)
|
|
156
156
|
c.tsw.WriteLiterally(": ")
|
|
157
157
|
typ := c.pkg.TypesInfo.TypeOf(field.Type)
|
|
158
|
-
c.WriteGoType(typ) // Use WriteGoType for field type
|
|
158
|
+
c.WriteGoType(typ, GoTypeContextGeneral) // Use WriteGoType for field type
|
|
159
159
|
|
|
160
160
|
if !isArguments {
|
|
161
161
|
// write tag comment if any for struct fields
|
package/compiler/lit.go
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
package compiler
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
+
"fmt"
|
|
4
5
|
"go/ast"
|
|
5
6
|
"go/token"
|
|
6
7
|
"strconv"
|
|
@@ -90,9 +91,40 @@ func (c *GoToTSCompiler) WriteFuncLitValue(exp *ast.FuncLit) error {
|
|
|
90
91
|
|
|
91
92
|
c.tsw.WriteLiterally(" => ")
|
|
92
93
|
|
|
94
|
+
hasNamedReturns := false
|
|
95
|
+
if exp.Type.Results != nil {
|
|
96
|
+
for _, field := range exp.Type.Results.List {
|
|
97
|
+
if len(field.Names) > 0 {
|
|
98
|
+
hasNamedReturns = true
|
|
99
|
+
break
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if hasNamedReturns {
|
|
105
|
+
c.tsw.WriteLine("{")
|
|
106
|
+
c.tsw.Indent(1)
|
|
107
|
+
|
|
108
|
+
// Declare named return variables and initialize them to their zero values
|
|
109
|
+
for _, field := range exp.Type.Results.List {
|
|
110
|
+
for _, name := range field.Names {
|
|
111
|
+
c.tsw.WriteLiterallyf("let %s: ", name.Name)
|
|
112
|
+
c.WriteTypeExpr(field.Type)
|
|
113
|
+
c.tsw.WriteLiterally(" = ")
|
|
114
|
+
c.WriteZeroValueForType(c.pkg.TypesInfo.TypeOf(field.Type))
|
|
115
|
+
c.tsw.WriteLine("")
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
93
120
|
// Write function body
|
|
94
|
-
if err := c.
|
|
95
|
-
return err
|
|
121
|
+
if err := c.WriteStmtBlock(exp.Body, false); err != nil {
|
|
122
|
+
return fmt.Errorf("failed to write block statement: %w", err)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if hasNamedReturns {
|
|
126
|
+
c.tsw.Indent(-1)
|
|
127
|
+
c.tsw.WriteLiterally("}")
|
|
96
128
|
}
|
|
97
129
|
|
|
98
130
|
return nil
|
package/compiler/primitive.go
CHANGED
|
@@ -29,7 +29,10 @@ var goToTypescriptPrimitives = map[string]string{
|
|
|
29
29
|
"int16": "number",
|
|
30
30
|
"int32": "number",
|
|
31
31
|
"rune": "number", // alias for int32
|
|
32
|
-
|
|
32
|
+
|
|
33
|
+
// TODO: add bigint support
|
|
34
|
+
// "int64": "bigint", // Requires TypeScript target >= ES2020
|
|
35
|
+
"int64": "number",
|
|
33
36
|
|
|
34
37
|
// Unsigned Integers
|
|
35
38
|
"uint": "number",
|
|
@@ -37,7 +40,10 @@ var goToTypescriptPrimitives = map[string]string{
|
|
|
37
40
|
"byte": "number",
|
|
38
41
|
"uint16": "number",
|
|
39
42
|
"uint32": "number",
|
|
40
|
-
|
|
43
|
+
|
|
44
|
+
// TODO: add bigint support
|
|
45
|
+
// "uint64": "bigint", // Requires TypeScript target >= ES2020
|
|
46
|
+
"uint64": "number",
|
|
41
47
|
|
|
42
48
|
// Floating Point Numbers
|
|
43
49
|
"float32": "number",
|
|
@@ -76,16 +82,17 @@ func GoBuiltinToTypescript(typeName string) (string, bool) {
|
|
|
76
82
|
// in their respective expression/statement writers and might not be directly mapped here.
|
|
77
83
|
// Bitwise AND NOT (`&^=`) is also mapped but may require specific runtime support if not directly translatable.
|
|
78
84
|
var tokenMap = map[token.Token]string{
|
|
79
|
-
token.ADD:
|
|
80
|
-
token.SUB:
|
|
81
|
-
token.MUL:
|
|
82
|
-
token.QUO:
|
|
83
|
-
token.REM:
|
|
84
|
-
token.AND:
|
|
85
|
-
token.OR:
|
|
86
|
-
token.XOR:
|
|
87
|
-
token.SHL:
|
|
88
|
-
token.SHR:
|
|
85
|
+
token.ADD: "+",
|
|
86
|
+
token.SUB: "-",
|
|
87
|
+
token.MUL: "*",
|
|
88
|
+
token.QUO: "/",
|
|
89
|
+
token.REM: "%",
|
|
90
|
+
token.AND: "&",
|
|
91
|
+
token.OR: "|",
|
|
92
|
+
token.XOR: "^",
|
|
93
|
+
token.SHL: "<<",
|
|
94
|
+
token.SHR: ">>",
|
|
95
|
+
token.AND_NOT: "& ~", // &^ operator: bitwise AND NOT
|
|
89
96
|
|
|
90
97
|
token.ADD_ASSIGN: "+=",
|
|
91
98
|
token.SUB_ASSIGN: "-=",
|