goscript 0.0.23 → 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 +1 -1
- package/compiler/analysis.go +73 -131
- package/compiler/analysis_test.go +220 -0
- package/compiler/assignment.go +37 -43
- package/compiler/builtin_test.go +102 -0
- package/compiler/compiler.go +79 -14
- package/compiler/composite-lit.go +108 -43
- package/compiler/config.go +7 -3
- package/compiler/config_test.go +6 -33
- package/compiler/expr-selector.go +66 -41
- package/compiler/expr-star.go +57 -65
- package/compiler/expr-type.go +1 -1
- package/compiler/expr-value.go +1 -1
- package/compiler/expr.go +79 -18
- package/compiler/primitive.go +11 -10
- package/compiler/spec-struct.go +3 -3
- package/compiler/spec-value.go +75 -29
- package/compiler/spec.go +9 -3
- package/compiler/stmt-assign.go +36 -2
- package/compiler/stmt-for.go +11 -0
- package/compiler/stmt-range.go +110 -0
- package/compiler/stmt.go +52 -0
- package/compiler/type.go +36 -11
- 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 +3 -2
package/compiler/expr-star.go
CHANGED
|
@@ -1,89 +1,81 @@
|
|
|
1
1
|
package compiler
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
-
"fmt"
|
|
5
4
|
"go/ast"
|
|
5
|
+
"go/types"
|
|
6
6
|
)
|
|
7
7
|
|
|
8
8
|
// WriteStarExpr translates a Go pointer dereference expression (`ast.StarExpr`, e.g., `*p`)
|
|
9
9
|
// into its TypeScript equivalent. This involves careful handling of Go's pointers
|
|
10
|
-
// and TypeScript's
|
|
10
|
+
// and TypeScript's varRefing mechanism for emulating pointer semantics.
|
|
11
11
|
//
|
|
12
|
-
// The translation depends on whether the pointer variable `p` itself is
|
|
12
|
+
// The translation depends on whether the pointer variable `p` itself is varrefed and
|
|
13
13
|
// what type of value it points to:
|
|
14
|
-
// 1. If `p` is not
|
|
15
|
-
// (`p` holds a
|
|
16
|
-
// 2. If `p` is not
|
|
14
|
+
// 1. If `p` is not varrefed and points to a primitive or another pointer: `*p` -> `p!.value`.
|
|
15
|
+
// (`p` holds a varRef, so dereference accesses its `value` field).
|
|
16
|
+
// 2. If `p` is not varrefed and points to a struct: `*p` -> `p!`.
|
|
17
17
|
// (`p` holds the struct instance directly; structs are reference types in TS).
|
|
18
|
-
// 3. If `p` is
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
// (First `.value` unboxes `p` to get the struct instance).
|
|
18
|
+
// 3. If `p` is variable referenced (i.e., `p` is `$.VarRef<PointerType>`) and points to a primitive/pointer:
|
|
19
|
+
// `p.value!.value` (access the variable reference, then dereference the pointer)
|
|
20
|
+
// 4. If `p` is varrefed and points to a struct: `p.value!`.
|
|
21
|
+
// (First `.value` unvarRefes `p` to get the struct instance).
|
|
23
22
|
//
|
|
24
|
-
// `WriteValueExpr(operand)` handles the initial
|
|
23
|
+
// `WriteValueExpr(operand)` handles the initial unvarRefing of `p` if `p` itself is a varrefed variable.
|
|
25
24
|
// A non-null assertion `!` is always added as pointers can be nil.
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
25
|
+
// The function determines if `.value` access is needed by checking what the Go pointer operand points to.
|
|
26
|
+
//
|
|
27
|
+
// For multi-level dereferences like `***p`, this function is called recursively, with each level
|
|
28
|
+
// adding the appropriate `!.value` suffix.
|
|
29
|
+
//
|
|
30
|
+
// Examples:
|
|
31
|
+
// - Simple pointer to primitive: `p!.value` (where p is *int)
|
|
32
|
+
// - Variable referenced pointer to primitive: `p.value!.value` (where p is VarRef<*int>)
|
|
33
|
+
// Example: let p = $.varRef(x) (where x is another variable reference) => p.value!.value
|
|
34
|
+
// - Pointer to struct: `p!` (where p is *MyStruct)
|
|
35
|
+
// Example: let p = $.varRef(new MyStruct()) => p.value!
|
|
36
|
+
// - Variable referenced pointer to struct: `p.value!` (where p is VarRef<*MyStruct>)
|
|
37
|
+
// - Triple pointer: `p3!.value!.value!.value` (where p3 is VarRef<VarRef<VarRef<number> | null> | null> | null)
|
|
29
38
|
func (c *GoToTSCompiler) WriteStarExpr(exp *ast.StarExpr) error {
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
// Example: let p = x (where x is a box) => p!.value
|
|
36
|
-
//
|
|
37
|
-
// 2. p! - when p is not boxed and points to a struct
|
|
38
|
-
// Example: let p = new MyStruct() => p! (structs are reference types)
|
|
39
|
-
//
|
|
40
|
-
// 3. p.value!.value - when p is boxed and points to a primitive/pointer
|
|
41
|
-
// Example: let p = $.box(x) (where x is another box) => p.value!.value
|
|
42
|
-
//
|
|
43
|
-
// 4. p.value! - when p is boxed and points to a struct
|
|
44
|
-
// Example: let p = $.box(new MyStruct()) => p.value!
|
|
45
|
-
//
|
|
46
|
-
// Critical bug fix: We must handle each case correctly to avoid over-dereferencing
|
|
47
|
-
// (adding too many .value) or under-dereferencing (missing .value where needed)
|
|
48
|
-
//
|
|
49
|
-
// NOTE: This logic aligns with design/BOXES_POINTERS.md.
|
|
50
|
-
|
|
51
|
-
// Get the operand expression and its type information
|
|
52
|
-
operand := exp.X
|
|
53
|
-
|
|
54
|
-
// Get the type of the operand (the pointer being dereferenced)
|
|
55
|
-
ptrType := c.pkg.TypesInfo.TypeOf(operand)
|
|
56
|
-
|
|
57
|
-
// Special case for handling multi-level dereferencing:
|
|
58
|
-
// Check if the operand is itself a StarExpr (e.g., **p or ***p)
|
|
59
|
-
// We need to handle these specially to correctly generate nested .value accesses
|
|
60
|
-
if starExpr, isStarExpr := operand.(*ast.StarExpr); isStarExpr {
|
|
61
|
-
// First, write the inner star expression
|
|
62
|
-
if err := c.WriteStarExpr(starExpr); err != nil {
|
|
63
|
-
return fmt.Errorf("failed to write inner star expression: %w", err)
|
|
39
|
+
// Check if the operand is an identifier that is varrefed
|
|
40
|
+
isVarrefedIdent := false
|
|
41
|
+
if ident, ok := exp.X.(*ast.Ident); ok {
|
|
42
|
+
if obj := c.pkg.TypesInfo.ObjectOf(ident); obj != nil {
|
|
43
|
+
isVarrefedIdent = c.analysis.NeedsVarRef(obj)
|
|
64
44
|
}
|
|
65
|
-
|
|
66
|
-
// Always add .value for multi-level dereferences
|
|
67
|
-
// For expressions like **p, each * adds a .value
|
|
68
|
-
c.tsw.WriteLiterally("!.value")
|
|
69
|
-
return nil
|
|
70
45
|
}
|
|
71
46
|
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
47
|
+
// Write the operand
|
|
48
|
+
if isVarrefedIdent {
|
|
49
|
+
// For varrefed identifiers, we need to access the value first
|
|
50
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
51
|
+
return err
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
// For non-varrefed identifiers and other expressions
|
|
55
|
+
switch operand := exp.X.(type) {
|
|
56
|
+
case *ast.Ident:
|
|
57
|
+
// Write identifier without .value access
|
|
58
|
+
c.WriteIdent(operand, false)
|
|
59
|
+
default:
|
|
60
|
+
// For other expressions (like nested star expressions), use WriteValueExpr
|
|
61
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
62
|
+
return err
|
|
63
|
+
}
|
|
64
|
+
}
|
|
77
65
|
}
|
|
78
66
|
|
|
79
|
-
// Add
|
|
67
|
+
// Add non-null assertion for pointer safety
|
|
80
68
|
c.tsw.WriteLiterally("!")
|
|
81
69
|
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
70
|
+
// Check what the operand points to (not what the result is)
|
|
71
|
+
operandType := c.pkg.TypesInfo.TypeOf(exp.X)
|
|
72
|
+
if ptrType, isPtr := operandType.(*types.Pointer); isPtr {
|
|
73
|
+
elemType := ptrType.Elem()
|
|
74
|
+
// Only add .value if NOT pointing to a struct
|
|
75
|
+
if _, isStruct := elemType.Underlying().(*types.Struct); !isStruct {
|
|
76
|
+
c.tsw.WriteLiterally(".value")
|
|
77
|
+
}
|
|
78
|
+
// If pointing to a struct, don't add .value (structs are reference types in TS)
|
|
87
79
|
}
|
|
88
80
|
|
|
89
81
|
return nil
|
package/compiler/expr-type.go
CHANGED
|
@@ -11,7 +11,7 @@ import (
|
|
|
11
11
|
// It handles various Go type expressions:
|
|
12
12
|
// - Basic types (e.g., int, string, bool) -> TypeScript primitives (number, string, boolean)
|
|
13
13
|
// - Named types -> TypeScript class/interface names
|
|
14
|
-
// - Pointer types (`*T`) -> `$.
|
|
14
|
+
// - Pointer types (`*T`) -> `$.VarRef<T_ts> | null`
|
|
15
15
|
// - Slice types (`[]T`) -> `$.Slice<T_ts>`
|
|
16
16
|
// - Array types (`[N]T`) -> `T_ts[]`
|
|
17
17
|
// - Map types (`map[K]V`) -> `Map<K_ts, V_ts>`
|
package/compiler/expr-value.go
CHANGED
|
@@ -7,7 +7,7 @@ import (
|
|
|
7
7
|
// WriteValueExpr translates a Go abstract syntax tree (AST) expression (`ast.Expr`)
|
|
8
8
|
// that represents a value into its TypeScript value equivalent.
|
|
9
9
|
// This is a central dispatch function for various expression types:
|
|
10
|
-
// - Identifiers (`ast.Ident`): Delegates to `WriteIdent`, potentially adding `.value` for
|
|
10
|
+
// - Identifiers (`ast.Ident`): Delegates to `WriteIdent`, potentially adding `.value` for varrefed variables.
|
|
11
11
|
// - Selector expressions (`ast.SelectorExpr`, e.g., `obj.Field` or `pkg.Var`): Delegates to `WriteSelectorExpr`.
|
|
12
12
|
// - Pointer dereferences (`ast.StarExpr`, e.g., `*ptr`): Delegates to `WriteStarExpr`.
|
|
13
13
|
// - Function calls (`ast.CallExpr`): Delegates to `WriteCallExpr`.
|
package/compiler/expr.go
CHANGED
|
@@ -157,7 +157,7 @@ func (c *GoToTSCompiler) WriteTypeAssertExpr(exp *ast.TypeAssertExpr) error {
|
|
|
157
157
|
// of the left (X) and right (Y) operands of the binary expression.
|
|
158
158
|
// Returns `true` if both operands are determined to be pointer types,
|
|
159
159
|
// `false` otherwise. This is used to apply specific comparison semantics
|
|
160
|
-
// for pointers (e.g., comparing the
|
|
160
|
+
// for pointers (e.g., comparing the varRef objects directly).
|
|
161
161
|
func (c *GoToTSCompiler) isPointerComparison(exp *ast.BinaryExpr) bool {
|
|
162
162
|
leftType := c.pkg.TypesInfo.TypeOf(exp.X)
|
|
163
163
|
rightType := c.pkg.TypesInfo.TypeOf(exp.Y)
|
|
@@ -195,10 +195,10 @@ func (c *GoToTSCompiler) getTypeNameString(typeExpr ast.Expr) string {
|
|
|
195
195
|
// It handles several cases:
|
|
196
196
|
// - Channel send (`ch <- val`): Becomes `await ch.send(val)`.
|
|
197
197
|
// - Nil comparison for pointers (`ptr == nil` or `ptr != nil`): Compares the
|
|
198
|
-
// pointer (which may be a
|
|
198
|
+
// pointer (which may be a varRef object or `null`) directly to `null` using
|
|
199
199
|
// the translated operator (`==` or `!=`).
|
|
200
200
|
// - Pointer comparison (non-nil, `ptr1 == ptr2` or `ptr1 != ptr2`): Compares
|
|
201
|
-
// the
|
|
201
|
+
// the varRef objects directly using strict equality (`===` or `!==`).
|
|
202
202
|
// - Bitwise operations (`&`, `|`, `^`, `<<`, `>>`, `&^`): The expression is wrapped
|
|
203
203
|
// in parentheses `()` to ensure correct precedence in TypeScript, and operators
|
|
204
204
|
// are mapped (e.g., `&^` might need special handling or is mapped to a runtime helper).
|
|
@@ -243,9 +243,26 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
|
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
if isNilComparison {
|
|
246
|
-
//
|
|
247
|
-
|
|
248
|
-
|
|
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
|
+
}
|
|
249
266
|
}
|
|
250
267
|
c.tsw.WriteLiterally(" ")
|
|
251
268
|
tokStr, ok := TokenToTs(exp.Op)
|
|
@@ -258,7 +275,7 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
|
|
|
258
275
|
}
|
|
259
276
|
|
|
260
277
|
// Check if this is a pointer comparison (non-nil)
|
|
261
|
-
// Compare the
|
|
278
|
+
// Compare the varRef objects directly using === or !==
|
|
262
279
|
if c.isPointerComparison(exp) {
|
|
263
280
|
c.tsw.WriteLiterally("(") // Wrap comparison
|
|
264
281
|
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
@@ -284,6 +301,50 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
|
|
|
284
301
|
return nil
|
|
285
302
|
}
|
|
286
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
|
+
|
|
287
348
|
// Check if the operator is a bitwise operator
|
|
288
349
|
isBitwise := false
|
|
289
350
|
switch exp.Op {
|
|
@@ -334,11 +395,11 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
|
|
|
334
395
|
// It handles several unary operations:
|
|
335
396
|
// - Channel receive (`<-ch`): Becomes `await ch.receive()`.
|
|
336
397
|
// - Address-of (`&var`):
|
|
337
|
-
// - If `var` is a
|
|
338
|
-
// to the
|
|
339
|
-
// - 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
|
|
340
401
|
// the operand `var`. The resulting TypeScript value (e.g., a new object instance)
|
|
341
|
-
// acts as the "pointer".
|
|
402
|
+
// acts as the "pointer". VarRefing decisions for such pointers are handled at
|
|
342
403
|
// the assignment site.
|
|
343
404
|
// - Other unary operators (`+`, `-`, `!`, `^`): Mapped to their TypeScript
|
|
344
405
|
// equivalents (e.g., `+`, `-`, `!`, `~` for bitwise NOT). Parentheses are added
|
|
@@ -359,26 +420,26 @@ func (c *GoToTSCompiler) WriteUnaryExpr(exp *ast.UnaryExpr) error {
|
|
|
359
420
|
}
|
|
360
421
|
|
|
361
422
|
if exp.Op == token.AND { // Address-of operator (&)
|
|
362
|
-
// If the operand is an identifier for a variable that is
|
|
363
|
-
// 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.
|
|
364
425
|
if ident, ok := exp.X.(*ast.Ident); ok {
|
|
365
426
|
var obj types.Object
|
|
366
427
|
obj = c.pkg.TypesInfo.Uses[ident]
|
|
367
428
|
if obj == nil {
|
|
368
429
|
obj = c.pkg.TypesInfo.Defs[ident]
|
|
369
430
|
}
|
|
370
|
-
if obj != nil && c.analysis.
|
|
371
|
-
// &
|
|
372
|
-
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)
|
|
373
434
|
return nil
|
|
374
435
|
}
|
|
375
436
|
}
|
|
376
437
|
|
|
377
|
-
// Otherwise (&
|
|
438
|
+
// Otherwise (&unvarrefedVar, &CompositeLit{}, &FuncCall(), etc.),
|
|
378
439
|
// the address-of operator in Go, when used to create a pointer,
|
|
379
440
|
// translates to simply evaluating the operand in TypeScript.
|
|
380
441
|
// The resulting value (e.g., a new object instance) acts as the "pointer".
|
|
381
|
-
//
|
|
442
|
+
// VarRefing decisions are handled at the assignment site based on the LHS variable.
|
|
382
443
|
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
383
444
|
return fmt.Errorf("failed to write &-operand: %w", err)
|
|
384
445
|
}
|
package/compiler/primitive.go
CHANGED
|
@@ -82,16 +82,17 @@ func GoBuiltinToTypescript(typeName string) (string, bool) {
|
|
|
82
82
|
// in their respective expression/statement writers and might not be directly mapped here.
|
|
83
83
|
// Bitwise AND NOT (`&^=`) is also mapped but may require specific runtime support if not directly translatable.
|
|
84
84
|
var tokenMap = map[token.Token]string{
|
|
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:
|
|
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
|
|
95
96
|
|
|
96
97
|
token.ADD_ASSIGN: "+=",
|
|
97
98
|
token.SUB_ASSIGN: "-=",
|
package/compiler/spec-struct.go
CHANGED
|
@@ -12,7 +12,7 @@ import (
|
|
|
12
12
|
// It handles the generation of:
|
|
13
13
|
// - The class declaration.
|
|
14
14
|
// - Getters and setters for all fields (both direct and embedded).
|
|
15
|
-
// - The internal `_fields` property, which stores field values in `$.
|
|
15
|
+
// - The internal `_fields` property, which stores field values in `$.VarRef` containers
|
|
16
16
|
// to maintain Go's value semantics.
|
|
17
17
|
// - A constructor that initializes the `_fields` and allows partial initialization.
|
|
18
18
|
// - A `clone` method for creating a deep copy of the struct instance.
|
|
@@ -83,7 +83,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
83
83
|
fieldKeyName = field.Name()
|
|
84
84
|
}
|
|
85
85
|
fieldTsType := c.getTypeString(field.Type())
|
|
86
|
-
c.tsw.WriteLinef("%s: $.
|
|
86
|
+
c.tsw.WriteLinef("%s: $.VarRef<%s>;", fieldKeyName, fieldTsType)
|
|
87
87
|
}
|
|
88
88
|
c.tsw.Indent(-1)
|
|
89
89
|
c.tsw.WriteLine("}")
|
|
@@ -111,7 +111,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
111
111
|
fieldKeyName = field.Name()
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
c.
|
|
114
|
+
c.writeVarRefedFieldInitializer(fieldKeyName, fieldType, field.Anonymous())
|
|
115
115
|
|
|
116
116
|
if i < numFields-1 {
|
|
117
117
|
c.tsw.WriteLine(",")
|
package/compiler/spec-value.go
CHANGED
|
@@ -12,13 +12,12 @@ import (
|
|
|
12
12
|
// declarations.
|
|
13
13
|
//
|
|
14
14
|
// For single variable declarations (`var x T = val` or `var x = val` or `var x T`):
|
|
15
|
-
// - It determines if the variable `x` needs to be
|
|
16
|
-
// using `c.analysis.
|
|
17
|
-
// - If
|
|
18
|
-
// The type annotation is `$.
|
|
19
|
-
// - If not
|
|
20
|
-
// The type annotation is `T_ts`. If the initializer is `&
|
|
21
|
-
// If the RHS is a struct value, `.clone()` is applied to maintain Go's value semantics.
|
|
15
|
+
// - It determines if the variable `x` needs to be varrefed (e.g., if its address is taken)
|
|
16
|
+
// using `c.analysis.NeedsVarRef(obj)`.
|
|
17
|
+
// - If variable referenced: `let x: $.VarRef<T_ts> = $.varRef(initializer_ts_or_zero_ts);`
|
|
18
|
+
// The type annotation is `$.VarRef<T_ts>`, and the initializer is wrapped in `$.varRef()`.
|
|
19
|
+
// - If not variable referenced: `let x: T_ts = initializer_ts_or_zero_ts;`
|
|
20
|
+
// The type annotation is `T_ts`. If the initializer is `&unvarrefedVar`, it becomes `$.varRef(unvarrefedVar_ts)`.
|
|
22
21
|
// - If no initializer is provided, the TypeScript zero value (from `WriteZeroValueForType`)
|
|
23
22
|
// is used.
|
|
24
23
|
// - Type `T` (or `T_ts`) is obtained from `obj.Type()` and translated via `WriteGoType`.
|
|
@@ -48,7 +47,7 @@ func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
|
|
|
48
47
|
}
|
|
49
48
|
|
|
50
49
|
goType := obj.Type()
|
|
51
|
-
|
|
50
|
+
needsVarRef := c.analysis.NeedsVarRef(obj) // Check if address is taken
|
|
52
51
|
|
|
53
52
|
hasInitializer := len(a.Values) > 0
|
|
54
53
|
var initializerExpr ast.Expr
|
|
@@ -86,17 +85,64 @@ func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
|
|
|
86
85
|
c.tsw.WriteLiterally("let ")
|
|
87
86
|
c.tsw.WriteLiterally(name.Name)
|
|
88
87
|
|
|
89
|
-
|
|
88
|
+
// Write type annotation if:
|
|
89
|
+
// 1. Not a slice conversion (normal case), OR
|
|
90
|
+
// 2. Is a slice conversion but needs varRefing (we need explicit type for $.varRef())
|
|
91
|
+
if !isSliceConversion || needsVarRef {
|
|
90
92
|
c.tsw.WriteLiterally(": ")
|
|
91
93
|
// Write type annotation
|
|
92
|
-
if
|
|
93
|
-
// If
|
|
94
|
-
c.tsw.WriteLiterally("$.
|
|
95
|
-
|
|
94
|
+
if needsVarRef {
|
|
95
|
+
// If varrefed, the variable holds VarRef<OriginalGoType>
|
|
96
|
+
c.tsw.WriteLiterally("$.VarRef<")
|
|
97
|
+
|
|
98
|
+
// Special case: if this is a slice conversion from an array type,
|
|
99
|
+
// we should use the slice type instead of the array type
|
|
100
|
+
if isSliceConversion {
|
|
101
|
+
if arrayType, isArray := goType.Underlying().(*types.Array); isArray {
|
|
102
|
+
// Convert [N]T to $.Slice<T>
|
|
103
|
+
c.tsw.WriteLiterally("$.Slice<")
|
|
104
|
+
c.WriteGoType(arrayType.Elem(), GoTypeContextGeneral)
|
|
105
|
+
c.tsw.WriteLiterally(">")
|
|
106
|
+
} else {
|
|
107
|
+
// For slice types, write as-is (already $.Slice<T>)
|
|
108
|
+
c.WriteGoType(goType, GoTypeContextGeneral)
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
c.WriteGoType(goType, GoTypeContextGeneral) // Write the original Go type T
|
|
112
|
+
}
|
|
96
113
|
c.tsw.WriteLiterally(">")
|
|
97
114
|
} else {
|
|
98
|
-
// If not
|
|
99
|
-
|
|
115
|
+
// If not varrefed, the variable holds the translated Go type directly
|
|
116
|
+
// Custom logic for non-var-ref'd pointers to structs/interfaces.
|
|
117
|
+
if ptrType, isPtr := goType.(*types.Pointer); isPtr {
|
|
118
|
+
elemType := ptrType.Elem()
|
|
119
|
+
actualElemType := elemType.Underlying() // Get the true underlying type (e.g., struct, interface, basic)
|
|
120
|
+
|
|
121
|
+
isStruct := false
|
|
122
|
+
if _, ok := actualElemType.(*types.Struct); ok {
|
|
123
|
+
isStruct = true
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
isInterface := false
|
|
127
|
+
if _, ok := actualElemType.(*types.Interface); ok {
|
|
128
|
+
isInterface = true
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if isStruct || isInterface {
|
|
132
|
+
// For non-var-ref'd pointers to structs or interfaces,
|
|
133
|
+
// the type is T | null, not $.VarRef<T> | null.
|
|
134
|
+
c.WriteGoType(elemType, GoTypeContextGeneral) // Write the element type itself (e.g., MyStruct)
|
|
135
|
+
c.tsw.WriteLiterally(" | null")
|
|
136
|
+
} else {
|
|
137
|
+
// For other pointer types (e.g., *int, *string, *[]int, **MyStruct),
|
|
138
|
+
// or pointers to types that are not structs/interfaces,
|
|
139
|
+
// use the standard pointer type translation.
|
|
140
|
+
c.WriteGoType(goType, GoTypeContextGeneral)
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
// Not a pointer type, write as is.
|
|
144
|
+
c.WriteGoType(goType, GoTypeContextGeneral)
|
|
145
|
+
}
|
|
100
146
|
}
|
|
101
147
|
}
|
|
102
148
|
|
|
@@ -121,41 +167,41 @@ func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
|
|
|
121
167
|
}
|
|
122
168
|
}
|
|
123
169
|
|
|
124
|
-
if
|
|
125
|
-
//
|
|
126
|
-
c.tsw.WriteLiterally("$.
|
|
170
|
+
if needsVarRef {
|
|
171
|
+
// VarRef variable: let v: VarRef<T> = $.varRef(init_or_zero);
|
|
172
|
+
c.tsw.WriteLiterally("$.varRef(")
|
|
127
173
|
if hasInitializer {
|
|
128
174
|
// Write the compiled initializer expression normally
|
|
129
175
|
if err := c.WriteValueExpr(initializerExpr); err != nil {
|
|
130
176
|
return err
|
|
131
177
|
}
|
|
132
178
|
} else {
|
|
133
|
-
// No initializer,
|
|
179
|
+
// No initializer, varRef the zero value
|
|
134
180
|
c.WriteZeroValueForType(goType)
|
|
135
181
|
}
|
|
136
182
|
c.tsw.WriteLiterally(")")
|
|
137
183
|
} else {
|
|
138
|
-
//
|
|
184
|
+
// Unvarrefed variable: let v: T = init_or_zero;
|
|
139
185
|
if hasInitializer {
|
|
140
|
-
// Handle &v initializer specifically for
|
|
186
|
+
// Handle &v initializer specifically for unvarrefed variables
|
|
141
187
|
if unaryExpr, isUnary := initializerExpr.(*ast.UnaryExpr); isUnary && unaryExpr.Op == token.AND {
|
|
142
188
|
// Initializer is &v
|
|
143
|
-
// Check if v is
|
|
144
|
-
|
|
189
|
+
// Check if v is varrefed
|
|
190
|
+
needsVarRefOperand := false
|
|
145
191
|
unaryExprXIdent, ok := unaryExpr.X.(*ast.Ident)
|
|
146
192
|
if ok {
|
|
147
193
|
innerObj := c.pkg.TypesInfo.Uses[unaryExprXIdent]
|
|
148
|
-
|
|
194
|
+
needsVarRefOperand = innerObj != nil && c.analysis.NeedsVarRef(innerObj)
|
|
149
195
|
}
|
|
150
196
|
|
|
151
|
-
// If v is
|
|
152
|
-
// If v is not
|
|
153
|
-
if
|
|
197
|
+
// If v is varrefed, assign the varRef itself (v)
|
|
198
|
+
// If v is not varrefed, assign $.varRef(v)
|
|
199
|
+
if needsVarRefOperand {
|
|
154
200
|
// special handling: do not write .value here.
|
|
155
201
|
c.WriteIdent(unaryExprXIdent, false)
|
|
156
202
|
} else {
|
|
157
|
-
// &
|
|
158
|
-
c.tsw.WriteLiterally("$.
|
|
203
|
+
// &unvarrefedVar -> $.varRef(unvarrefedVar)
|
|
204
|
+
c.tsw.WriteLiterally("$.varRef(")
|
|
159
205
|
if err := c.WriteValueExpr(unaryExpr.X); err != nil { // Write 'v'
|
|
160
206
|
return err
|
|
161
207
|
}
|
package/compiler/spec.go
CHANGED
|
@@ -78,9 +78,9 @@ func (c *GoToTSCompiler) writeGetterSetter(fieldName string, fieldType types.Typ
|
|
|
78
78
|
c.tsw.WriteLine("")
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
func (c *GoToTSCompiler)
|
|
81
|
+
func (c *GoToTSCompiler) writeVarRefedFieldInitializer(fieldName string, fieldType types.Type, isEmbedded bool) {
|
|
82
82
|
c.tsw.WriteLiterally(fieldName)
|
|
83
|
-
c.tsw.WriteLiterally(": $.
|
|
83
|
+
c.tsw.WriteLiterally(": $.varRef(")
|
|
84
84
|
|
|
85
85
|
if isEmbedded {
|
|
86
86
|
if _, isPtr := fieldType.(*types.Pointer); isPtr {
|
|
@@ -112,7 +112,7 @@ func (c *GoToTSCompiler) writeBoxedFieldInitializer(fieldName string, fieldType
|
|
|
112
112
|
|
|
113
113
|
func (c *GoToTSCompiler) writeClonedFieldInitializer(fieldName string, fieldType types.Type, isEmbedded bool) {
|
|
114
114
|
c.tsw.WriteLiterally(fieldName)
|
|
115
|
-
c.tsw.WriteLiterally(": $.
|
|
115
|
+
c.tsw.WriteLiterally(": $.varRef(")
|
|
116
116
|
|
|
117
117
|
if isEmbedded {
|
|
118
118
|
isPointerToStruct := false
|
|
@@ -183,6 +183,12 @@ func (c *GoToTSCompiler) WriteInterfaceTypeSpec(a *ast.TypeSpec, t *ast.Interfac
|
|
|
183
183
|
if err := c.WriteValueExpr(a.Name); err != nil {
|
|
184
184
|
return err
|
|
185
185
|
}
|
|
186
|
+
|
|
187
|
+
// Write type parameters if present (for generics)
|
|
188
|
+
if a.TypeParams != nil {
|
|
189
|
+
c.WriteTypeParameters(a.TypeParams)
|
|
190
|
+
}
|
|
191
|
+
|
|
186
192
|
c.tsw.WriteLiterally(" = ")
|
|
187
193
|
// Get the types.Interface from the ast.InterfaceType.
|
|
188
194
|
// For an interface definition like `type MyInterface interface { M() }`,
|
package/compiler/stmt-assign.go
CHANGED
|
@@ -33,7 +33,7 @@ import (
|
|
|
33
33
|
// - Uses `writeAssignmentCore` which handles:
|
|
34
34
|
// - Blank identifier `_` on LHS (evaluates RHS for side effects).
|
|
35
35
|
// - Assignment to dereferenced pointer `*p = val` -> `p_ts!.value = val_ts`.
|
|
36
|
-
// - Short declaration `x := y`: `let x = y_ts;`. If `x` is
|
|
36
|
+
// - Short declaration `x := y`: `let x = y_ts;`. If `x` is variable referenced, `let x: $.VarRef<T> = $.varRef(y_ts);`.
|
|
37
37
|
// - Regular assignment `x = y`, including compound assignments like `x += y`.
|
|
38
38
|
// - Assignment to map index `m[k] = v` using `$.mapSet`.
|
|
39
39
|
// - Struct value assignment `s1 = s2` becomes `s1 = s2.clone()` if `s2` is a struct.
|
|
@@ -44,7 +44,7 @@ import (
|
|
|
44
44
|
// The function ensures that the number of LHS and RHS expressions matches for
|
|
45
45
|
// most cases, erroring if they don't, except for specifically handled patterns
|
|
46
46
|
// like multi-assign from single call or discarded channel receive.
|
|
47
|
-
// It correctly applies `let` for `:=` (define) tokens and handles
|
|
47
|
+
// It correctly applies `let` for `:=` (define) tokens and handles varRefing and
|
|
48
48
|
// cloning semantics based on type information and analysis.
|
|
49
49
|
func (c *GoToTSCompiler) WriteStmtAssign(exp *ast.AssignStmt) error {
|
|
50
50
|
// writeMultiVarAssignFromCall handles multi-variable assignment from a single function call.
|
|
@@ -147,6 +147,10 @@ func (c *GoToTSCompiler) WriteStmtAssign(exp *ast.AssignStmt) error {
|
|
|
147
147
|
hasSelectors = true
|
|
148
148
|
break
|
|
149
149
|
}
|
|
150
|
+
if _, ok := lhsExpr.(*ast.StarExpr); ok {
|
|
151
|
+
hasSelectors = true
|
|
152
|
+
break
|
|
153
|
+
}
|
|
150
154
|
}
|
|
151
155
|
|
|
152
156
|
// If we have selector expressions, we need to ensure variables are initialized
|
|
@@ -176,6 +180,21 @@ func (c *GoToTSCompiler) WriteStmtAssign(exp *ast.AssignStmt) error {
|
|
|
176
180
|
if err := c.WriteValueExpr(selectorExpr); err != nil {
|
|
177
181
|
return fmt.Errorf("failed to write selector expression in LHS: %w", err)
|
|
178
182
|
}
|
|
183
|
+
} else if starExpr, ok := lhsExpr.(*ast.StarExpr); ok {
|
|
184
|
+
// Handle pointer dereference assignment: *p = value becomes p!.value = value
|
|
185
|
+
// Write the pointer variable directly without using WriteValueExpr
|
|
186
|
+
// because we don't want automatic .value access here
|
|
187
|
+
switch operand := starExpr.X.(type) {
|
|
188
|
+
case *ast.Ident:
|
|
189
|
+
// Write identifier without .value access
|
|
190
|
+
c.WriteIdent(operand, false)
|
|
191
|
+
default:
|
|
192
|
+
// For other expressions, use WriteValueExpr
|
|
193
|
+
if err := c.WriteValueExpr(starExpr.X); err != nil {
|
|
194
|
+
return fmt.Errorf("failed to write star expression X in LHS: %w", err)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
c.tsw.WriteLiterally("!.value")
|
|
179
198
|
} else {
|
|
180
199
|
return errors.Errorf("unhandled LHS expression in assignment: %T", lhsExpr)
|
|
181
200
|
}
|
|
@@ -211,6 +230,21 @@ func (c *GoToTSCompiler) WriteStmtAssign(exp *ast.AssignStmt) error {
|
|
|
211
230
|
if err := c.WriteValueExpr(selectorExpr); err != nil {
|
|
212
231
|
return fmt.Errorf("failed to write selector expression in LHS: %w", err)
|
|
213
232
|
}
|
|
233
|
+
} else if starExpr, ok := lhsExpr.(*ast.StarExpr); ok {
|
|
234
|
+
// Handle pointer dereference in destructuring: *p becomes p!.value
|
|
235
|
+
// Write the pointer variable directly without using WriteValueExpr
|
|
236
|
+
// because we don't want automatic .value access here
|
|
237
|
+
switch operand := starExpr.X.(type) {
|
|
238
|
+
case *ast.Ident:
|
|
239
|
+
// Write identifier without .value access
|
|
240
|
+
c.WriteIdent(operand, false)
|
|
241
|
+
default:
|
|
242
|
+
// For other expressions, use WriteValueExpr
|
|
243
|
+
if err := c.WriteValueExpr(starExpr.X); err != nil {
|
|
244
|
+
return fmt.Errorf("failed to write star expression X in destructuring: %w", err)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
c.tsw.WriteLiterally("!.value")
|
|
214
248
|
} else {
|
|
215
249
|
// Should not happen for valid Go code in this context, but handle defensively
|
|
216
250
|
return errors.Errorf("unhandled LHS expression in destructuring: %T", lhsExpr)
|