goscript 0.0.23 → 0.0.25
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 +2 -2
- package/cmd/goscript/cmd_compile.go +18 -2
- package/compiler/analysis.go +74 -132
- package/compiler/analysis_test.go +220 -0
- package/compiler/assignment.go +37 -43
- package/compiler/builtin_test.go +90 -0
- package/compiler/compiler.go +307 -22
- package/compiler/composite-lit.go +108 -43
- package/compiler/config.go +7 -3
- package/compiler/config_test.go +6 -33
- package/compiler/decl.go +7 -1
- package/compiler/expr-call.go +212 -2
- 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 +125 -20
- package/compiler/field.go +4 -4
- 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 +314 -1
- package/compiler/stmt.go +52 -0
- package/compiler/type.go +83 -15
- package/dist/gs/builtin/builtin.d.ts +9 -0
- package/dist/gs/builtin/builtin.js +46 -0
- package/dist/gs/builtin/builtin.js.map +1 -0
- package/dist/gs/builtin/channel.d.ts +193 -0
- package/dist/gs/builtin/channel.js +471 -0
- package/dist/gs/builtin/channel.js.map +1 -0
- package/dist/gs/builtin/defer.d.ts +38 -0
- package/dist/gs/builtin/defer.js +54 -0
- package/dist/gs/builtin/defer.js.map +1 -0
- package/dist/gs/builtin/index.d.ts +1 -0
- package/dist/gs/builtin/index.js +2 -0
- package/dist/gs/builtin/index.js.map +1 -0
- package/dist/gs/builtin/io.d.ts +16 -0
- package/dist/gs/builtin/io.js +15 -0
- package/dist/gs/builtin/io.js.map +1 -0
- package/dist/gs/builtin/map.d.ts +33 -0
- package/dist/gs/builtin/map.js +44 -0
- package/dist/gs/builtin/map.js.map +1 -0
- package/dist/gs/builtin/slice.d.ts +173 -0
- package/dist/gs/builtin/slice.js +799 -0
- package/dist/gs/builtin/slice.js.map +1 -0
- package/dist/gs/builtin/type.d.ts +203 -0
- package/dist/gs/builtin/type.js +744 -0
- package/dist/gs/builtin/type.js.map +1 -0
- package/dist/gs/builtin/varRef.d.ts +14 -0
- package/dist/gs/builtin/varRef.js +14 -0
- package/dist/gs/builtin/varRef.js.map +1 -0
- package/dist/gs/cmp/index.d.ts +4 -0
- package/dist/gs/cmp/index.js +27 -0
- package/dist/gs/cmp/index.js.map +1 -0
- package/dist/gs/context/context.d.ts +26 -0
- package/dist/gs/context/context.js +305 -0
- package/dist/gs/context/context.js.map +1 -0
- package/dist/gs/context/index.d.ts +1 -0
- package/dist/gs/context/index.js +2 -0
- package/dist/gs/context/index.js.map +1 -0
- package/dist/gs/internal/goarch/index.d.ts +6 -0
- package/dist/gs/internal/goarch/index.js +14 -0
- package/dist/gs/internal/goarch/index.js.map +1 -0
- package/dist/gs/iter/index.d.ts +1 -0
- package/dist/gs/iter/index.js +2 -0
- package/dist/gs/iter/index.js.map +1 -0
- package/dist/gs/iter/iter.d.ts +4 -0
- package/dist/gs/iter/iter.js +91 -0
- package/dist/gs/iter/iter.js.map +1 -0
- package/dist/gs/math/bits/index.d.ts +47 -0
- package/dist/gs/math/bits/index.js +298 -0
- package/dist/gs/math/bits/index.js.map +1 -0
- package/dist/gs/runtime/index.d.ts +1 -0
- package/dist/gs/runtime/index.js +2 -0
- package/dist/gs/runtime/index.js.map +1 -0
- package/dist/gs/runtime/runtime.d.ts +41 -0
- package/dist/gs/runtime/runtime.js +158 -0
- package/dist/gs/runtime/runtime.js.map +1 -0
- package/dist/gs/slices/index.d.ts +1 -0
- package/dist/gs/slices/index.js +2 -0
- package/dist/gs/slices/index.js.map +1 -0
- package/dist/gs/slices/slices.d.ts +8 -0
- package/dist/gs/slices/slices.js +20 -0
- package/dist/gs/slices/slices.js.map +1 -0
- package/dist/gs/time/index.d.ts +1 -0
- package/dist/gs/time/index.js +2 -0
- package/dist/gs/time/index.js.map +1 -0
- package/dist/gs/time/time.d.ts +57 -0
- package/dist/gs/time/time.js +208 -0
- package/dist/gs/time/time.js.map +1 -0
- package/package.json +3 -2
|
@@ -13,16 +13,16 @@ import (
|
|
|
13
13
|
// access on an object or struct.
|
|
14
14
|
// - For package selectors, it writes `PackageName.IdentifierName`. The `IdentifierName`
|
|
15
15
|
// is written using `WriteIdent` which handles potential `.value` access if the
|
|
16
|
-
// package-level variable is
|
|
16
|
+
// package-level variable is varrefed.
|
|
17
17
|
// - For field or method access on an object (`exp.X`), it first writes the base
|
|
18
|
-
// expression (`exp.X`) using `WriteValueExpr` (which handles its own
|
|
18
|
+
// expression (`exp.X`) using `WriteValueExpr` (which handles its own varRefing).
|
|
19
19
|
// Then, it writes a dot (`.`) followed by the selected identifier (`exp.Sel`)
|
|
20
|
-
// using `WriteIdent`, which appends `.value` if the field itself is
|
|
20
|
+
// using `WriteIdent`, which appends `.value` if the field itself is varrefed
|
|
21
21
|
// (e.g., accessing a field of primitive type through a pointer to a struct
|
|
22
22
|
// where the field's address might have been taken).
|
|
23
23
|
//
|
|
24
24
|
// This function aims to correctly navigate Go's automatic dereferencing and
|
|
25
|
-
// TypeScript's explicit
|
|
25
|
+
// TypeScript's explicit varRefing model.
|
|
26
26
|
func (c *GoToTSCompiler) WriteSelectorExpr(exp *ast.SelectorExpr) error {
|
|
27
27
|
// Check if this is a package selector (e.g., time.Now)
|
|
28
28
|
if pkgIdent, isPkgIdent := exp.X.(*ast.Ident); isPkgIdent {
|
|
@@ -31,62 +31,89 @@ func (c *GoToTSCompiler) WriteSelectorExpr(exp *ast.SelectorExpr) error {
|
|
|
31
31
|
// Package selectors should never use .value on the package name
|
|
32
32
|
c.tsw.WriteLiterally(pkgIdent.Name)
|
|
33
33
|
c.tsw.WriteLiterally(".")
|
|
34
|
-
// Write the selected identifier, allowing .value if it's a
|
|
34
|
+
// Write the selected identifier, allowing .value if it's a varrefed package variable
|
|
35
35
|
c.WriteIdent(exp.Sel, true)
|
|
36
36
|
return nil
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
// --- Special case for dereferenced pointer to struct with field access: (*p).field ---
|
|
41
|
+
// --- Special case for dereferenced pointer to struct with field access: (*p).field or (**p).field etc ---
|
|
42
42
|
var baseExpr ast.Expr = exp.X
|
|
43
43
|
// Look inside parentheses if present
|
|
44
44
|
if parenExpr, isParen := exp.X.(*ast.ParenExpr); isParen {
|
|
45
45
|
baseExpr = parenExpr.X
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
// Check if we have one or more star expressions (dereferences)
|
|
48
49
|
if starExpr, isStarExpr := baseExpr.(*ast.StarExpr); isStarExpr {
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
ptrObj = c.pkg.TypesInfo.ObjectOf(ptrIdent)
|
|
61
|
-
}
|
|
50
|
+
// Count the levels of dereference and find the innermost expression
|
|
51
|
+
dereferenceCount := 0
|
|
52
|
+
currentExpr := baseExpr
|
|
53
|
+
for {
|
|
54
|
+
if star, ok := currentExpr.(*ast.StarExpr); ok {
|
|
55
|
+
dereferenceCount++
|
|
56
|
+
currentExpr = star.X
|
|
57
|
+
} else {
|
|
58
|
+
break
|
|
59
|
+
}
|
|
60
|
+
}
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
// Get the type of the innermost expression (the pointer variable)
|
|
63
|
+
innerType := c.pkg.TypesInfo.TypeOf(currentExpr)
|
|
64
|
+
if innerType != nil {
|
|
65
|
+
// Check if after all dereferences we end up with a struct
|
|
66
|
+
finalType := innerType
|
|
67
|
+
for i := 0; i < dereferenceCount; i++ {
|
|
68
|
+
if ptrType, ok := finalType.(*types.Pointer); ok {
|
|
69
|
+
finalType = ptrType.Elem()
|
|
70
|
+
} else {
|
|
71
|
+
break
|
|
72
|
+
}
|
|
73
|
+
}
|
|
67
74
|
|
|
68
|
-
|
|
69
|
-
|
|
75
|
+
// If the final type is a struct, handle field access specially
|
|
76
|
+
if _, isStruct := finalType.Underlying().(*types.Struct); isStruct {
|
|
77
|
+
// Write the fully dereferenced expression
|
|
78
|
+
if err := c.WriteValueExpr(starExpr); err != nil {
|
|
79
|
+
return fmt.Errorf("failed to write dereferenced expression for field access: %w", err)
|
|
80
|
+
}
|
|
70
81
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
82
|
+
// Check if we need an extra .value for varrefed struct access
|
|
83
|
+
// This happens when the struct being pointed to is varrefed
|
|
84
|
+
needsExtraValue := false
|
|
85
|
+
if ident, ok := currentExpr.(*ast.Ident); ok {
|
|
86
|
+
if obj := c.pkg.TypesInfo.ObjectOf(ident); obj != nil {
|
|
87
|
+
// Check if after dereferencing, we get a varrefed struct
|
|
88
|
+
ptrType := obj.Type()
|
|
89
|
+
for i := 0; i < dereferenceCount; i++ {
|
|
90
|
+
if ptr, ok := ptrType.(*types.Pointer); ok {
|
|
91
|
+
ptrType = ptr.Elem()
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// If the final pointed-to type suggests the struct is varrefed
|
|
95
|
+
// (i.e., the dereference operation results in VarRef<Struct>)
|
|
96
|
+
if c.analysis.NeedsVarRefAccess(obj) {
|
|
97
|
+
needsExtraValue = true
|
|
75
98
|
}
|
|
76
|
-
|
|
77
|
-
// Add .field
|
|
78
|
-
c.tsw.WriteLiterally(".")
|
|
79
|
-
c.WriteIdent(exp.Sel, false) // Don't add .value to the field itself
|
|
80
|
-
return nil
|
|
81
99
|
}
|
|
82
100
|
}
|
|
101
|
+
|
|
102
|
+
if needsExtraValue {
|
|
103
|
+
c.tsw.WriteLiterally("!.value")
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Add .field
|
|
107
|
+
c.tsw.WriteLiterally(".")
|
|
108
|
+
c.WriteIdent(exp.Sel, false) // Don't add .value to the field itself
|
|
109
|
+
return nil
|
|
83
110
|
}
|
|
84
111
|
}
|
|
85
112
|
}
|
|
86
113
|
// --- End Special Case ---
|
|
87
114
|
|
|
88
115
|
// Fallback / Normal Case (e.g., obj.Field, pkg.Var, method calls)
|
|
89
|
-
// WriteValueExpr handles adding .value for the base variable itself if it's
|
|
116
|
+
// WriteValueExpr handles adding .value for the base variable itself if it's varrefed.
|
|
90
117
|
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
91
118
|
return fmt.Errorf("failed to write selector base expression: %w", err)
|
|
92
119
|
}
|
|
@@ -115,11 +142,9 @@ func (c *GoToTSCompiler) WriteSelectorExpr(exp *ast.SelectorExpr) error {
|
|
|
115
142
|
}
|
|
116
143
|
|
|
117
144
|
// Write the field/method name.
|
|
118
|
-
// Pass '
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
|
|
122
|
-
// relies on NeedsBoxedAccess for the field 'Val', which should typically be false.
|
|
123
|
-
c.WriteIdent(exp.Sel, true)
|
|
145
|
+
// Pass 'false' to WriteIdent to NOT add '.value' for struct fields.
|
|
146
|
+
// Struct fields use getters/setters, so we don't want to add .value here.
|
|
147
|
+
// The setter will handle the internal .value access.
|
|
148
|
+
c.WriteIdent(exp.Sel, false)
|
|
124
149
|
return nil
|
|
125
150
|
}
|
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
|
@@ -71,8 +71,44 @@ func (c *GoToTSCompiler) WriteIndexExpr(exp *ast.IndexExpr) error {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
// Check if it's a type parameter with a union constraint (e.g., string | []byte)
|
|
74
|
-
if
|
|
75
|
-
//
|
|
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 mixed string/byte constraint (like string | []byte)
|
|
81
|
+
if hasMixedStringByteConstraint(iface) {
|
|
82
|
+
// For mixed constraints, use specialized function that returns number (byte value)
|
|
83
|
+
c.tsw.WriteLiterally("$.indexStringOrBytes(")
|
|
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
|
+
return nil
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Check if the constraint includes only slice types (pure slice constraint)
|
|
96
|
+
if hasSliceConstraint(iface) {
|
|
97
|
+
// This is a pure slice type parameter, use regular slice indexing
|
|
98
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
99
|
+
return err
|
|
100
|
+
}
|
|
101
|
+
c.tsw.WriteLiterally("![") // non-null assertion
|
|
102
|
+
if err := c.WriteValueExpr(exp.Index); err != nil {
|
|
103
|
+
return err
|
|
104
|
+
}
|
|
105
|
+
c.tsw.WriteLiterally("]")
|
|
106
|
+
return nil
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// For other type parameters, use specialized function as fallback
|
|
76
112
|
// that returns number (byte value) for better TypeScript typing
|
|
77
113
|
c.tsw.WriteLiterally("$.indexStringOrBytes(")
|
|
78
114
|
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
@@ -157,7 +193,7 @@ func (c *GoToTSCompiler) WriteTypeAssertExpr(exp *ast.TypeAssertExpr) error {
|
|
|
157
193
|
// of the left (X) and right (Y) operands of the binary expression.
|
|
158
194
|
// Returns `true` if both operands are determined to be pointer types,
|
|
159
195
|
// `false` otherwise. This is used to apply specific comparison semantics
|
|
160
|
-
// for pointers (e.g., comparing the
|
|
196
|
+
// for pointers (e.g., comparing the varRef objects directly).
|
|
161
197
|
func (c *GoToTSCompiler) isPointerComparison(exp *ast.BinaryExpr) bool {
|
|
162
198
|
leftType := c.pkg.TypesInfo.TypeOf(exp.X)
|
|
163
199
|
rightType := c.pkg.TypesInfo.TypeOf(exp.Y)
|
|
@@ -195,10 +231,10 @@ func (c *GoToTSCompiler) getTypeNameString(typeExpr ast.Expr) string {
|
|
|
195
231
|
// It handles several cases:
|
|
196
232
|
// - Channel send (`ch <- val`): Becomes `await ch.send(val)`.
|
|
197
233
|
// - Nil comparison for pointers (`ptr == nil` or `ptr != nil`): Compares the
|
|
198
|
-
// pointer (which may be a
|
|
234
|
+
// pointer (which may be a varRef object or `null`) directly to `null` using
|
|
199
235
|
// the translated operator (`==` or `!=`).
|
|
200
236
|
// - Pointer comparison (non-nil, `ptr1 == ptr2` or `ptr1 != ptr2`): Compares
|
|
201
|
-
// the
|
|
237
|
+
// the varRef objects directly using strict equality (`===` or `!==`).
|
|
202
238
|
// - Bitwise operations (`&`, `|`, `^`, `<<`, `>>`, `&^`): The expression is wrapped
|
|
203
239
|
// in parentheses `()` to ensure correct precedence in TypeScript, and operators
|
|
204
240
|
// are mapped (e.g., `&^` might need special handling or is mapped to a runtime helper).
|
|
@@ -243,9 +279,26 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
|
|
|
243
279
|
}
|
|
244
280
|
|
|
245
281
|
if isNilComparison {
|
|
246
|
-
//
|
|
247
|
-
|
|
248
|
-
|
|
282
|
+
// For nil comparisons, we need to decide whether to write .value or not
|
|
283
|
+
// If the pointer variable is varrefed, we need to access .value
|
|
284
|
+
if ident, ok := ptrExpr.(*ast.Ident); ok {
|
|
285
|
+
if obj := c.pkg.TypesInfo.ObjectOf(ident); obj != nil {
|
|
286
|
+
if c.analysis.NeedsVarRef(obj) {
|
|
287
|
+
// Variable is varrefed, so we need to access .value
|
|
288
|
+
c.WriteIdent(ident, true) // This will add .value
|
|
289
|
+
} else {
|
|
290
|
+
// Variable is not varrefed, write directly
|
|
291
|
+
c.WriteIdent(ident, false)
|
|
292
|
+
}
|
|
293
|
+
} else {
|
|
294
|
+
// No object info, write directly
|
|
295
|
+
c.WriteIdent(ident, false)
|
|
296
|
+
}
|
|
297
|
+
} else {
|
|
298
|
+
// For other expressions, use WriteValueExpr (but this might need review)
|
|
299
|
+
if err := c.WriteValueExpr(ptrExpr); err != nil {
|
|
300
|
+
return fmt.Errorf("failed to write pointer expression in nil comparison: %w", err)
|
|
301
|
+
}
|
|
249
302
|
}
|
|
250
303
|
c.tsw.WriteLiterally(" ")
|
|
251
304
|
tokStr, ok := TokenToTs(exp.Op)
|
|
@@ -258,7 +311,7 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
|
|
|
258
311
|
}
|
|
259
312
|
|
|
260
313
|
// Check if this is a pointer comparison (non-nil)
|
|
261
|
-
// Compare the
|
|
314
|
+
// Compare the varRef objects directly using === or !==
|
|
262
315
|
if c.isPointerComparison(exp) {
|
|
263
316
|
c.tsw.WriteLiterally("(") // Wrap comparison
|
|
264
317
|
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
@@ -284,6 +337,50 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
|
|
|
284
337
|
return nil
|
|
285
338
|
}
|
|
286
339
|
|
|
340
|
+
// Check for Duration arithmetic operations (multiplication)
|
|
341
|
+
if exp.Op == token.MUL && c.pkg != nil && c.pkg.TypesInfo != nil {
|
|
342
|
+
leftType := c.pkg.TypesInfo.TypeOf(exp.X)
|
|
343
|
+
rightType := c.pkg.TypesInfo.TypeOf(exp.Y)
|
|
344
|
+
|
|
345
|
+
// Check if left operand is a Duration type (from time package)
|
|
346
|
+
if leftType != nil {
|
|
347
|
+
if namedType, ok := leftType.(*types.Named); ok {
|
|
348
|
+
if namedType.Obj().Pkg() != nil && namedType.Obj().Pkg().Path() == "time" && namedType.Obj().Name() == "Duration" {
|
|
349
|
+
// Duration * number -> Duration.multiply(duration, number)
|
|
350
|
+
c.tsw.WriteLiterally("$.multiplyDuration(")
|
|
351
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
352
|
+
return fmt.Errorf("failed to write Duration in multiplication: %w", err)
|
|
353
|
+
}
|
|
354
|
+
c.tsw.WriteLiterally(", ")
|
|
355
|
+
if err := c.WriteValueExpr(exp.Y); err != nil {
|
|
356
|
+
return fmt.Errorf("failed to write multiplier in Duration multiplication: %w", err)
|
|
357
|
+
}
|
|
358
|
+
c.tsw.WriteLiterally(")")
|
|
359
|
+
return nil
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Check if right operand is a Duration type (number * Duration)
|
|
365
|
+
if rightType != nil {
|
|
366
|
+
if namedType, ok := rightType.(*types.Named); ok {
|
|
367
|
+
if namedType.Obj().Pkg() != nil && namedType.Obj().Pkg().Path() == "time" && namedType.Obj().Name() == "Duration" {
|
|
368
|
+
// number * Duration -> Duration.multiply(duration, number)
|
|
369
|
+
c.tsw.WriteLiterally("$.multiplyDuration(")
|
|
370
|
+
if err := c.WriteValueExpr(exp.Y); err != nil {
|
|
371
|
+
return fmt.Errorf("failed to write Duration in multiplication: %w", err)
|
|
372
|
+
}
|
|
373
|
+
c.tsw.WriteLiterally(", ")
|
|
374
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
375
|
+
return fmt.Errorf("failed to write multiplier in Duration multiplication: %w", err)
|
|
376
|
+
}
|
|
377
|
+
c.tsw.WriteLiterally(")")
|
|
378
|
+
return nil
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
287
384
|
// Check if the operator is a bitwise operator
|
|
288
385
|
isBitwise := false
|
|
289
386
|
switch exp.Op {
|
|
@@ -316,6 +413,7 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
|
|
|
316
413
|
if !ok {
|
|
317
414
|
return errors.Errorf("unhandled binary op: %s", exp.Op.String())
|
|
318
415
|
}
|
|
416
|
+
|
|
319
417
|
c.tsw.WriteLiterally(tokStr)
|
|
320
418
|
c.tsw.WriteLiterally(" ")
|
|
321
419
|
if err := c.WriteValueExpr(exp.Y); err != nil {
|
|
@@ -334,11 +432,11 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
|
|
|
334
432
|
// It handles several unary operations:
|
|
335
433
|
// - Channel receive (`<-ch`): Becomes `await ch.receive()`.
|
|
336
434
|
// - Address-of (`&var`):
|
|
337
|
-
// - If `var` is a
|
|
338
|
-
// to the
|
|
339
|
-
// - Otherwise (e.g., `&
|
|
435
|
+
// - If `var` is a varrefed variable (its address was taken), `&var` evaluates
|
|
436
|
+
// to the varRef itself (i.e., `varName` in TypeScript, which holds the varRef).
|
|
437
|
+
// - Otherwise (e.g., `&unvarrefedVar`, `&MyStruct{}`, `&FuncCall()`), it evaluates
|
|
340
438
|
// the operand `var`. The resulting TypeScript value (e.g., a new object instance)
|
|
341
|
-
// acts as the "pointer".
|
|
439
|
+
// acts as the "pointer". VarRefing decisions for such pointers are handled at
|
|
342
440
|
// the assignment site.
|
|
343
441
|
// - Other unary operators (`+`, `-`, `!`, `^`): Mapped to their TypeScript
|
|
344
442
|
// equivalents (e.g., `+`, `-`, `!`, `~` for bitwise NOT). Parentheses are added
|
|
@@ -359,26 +457,26 @@ func (c *GoToTSCompiler) WriteUnaryExpr(exp *ast.UnaryExpr) error {
|
|
|
359
457
|
}
|
|
360
458
|
|
|
361
459
|
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
|
|
460
|
+
// If the operand is an identifier for a variable that is varrefed,
|
|
461
|
+
// the result of & is the varRef itself.
|
|
364
462
|
if ident, ok := exp.X.(*ast.Ident); ok {
|
|
365
463
|
var obj types.Object
|
|
366
464
|
obj = c.pkg.TypesInfo.Uses[ident]
|
|
367
465
|
if obj == nil {
|
|
368
466
|
obj = c.pkg.TypesInfo.Defs[ident]
|
|
369
467
|
}
|
|
370
|
-
if obj != nil && c.analysis.
|
|
371
|
-
// &
|
|
372
|
-
c.tsw.WriteLiterally(ident.Name) // Write the identifier name (which holds the
|
|
468
|
+
if obj != nil && c.analysis.NeedsVarRef(obj) {
|
|
469
|
+
// &varRefVar -> varRefVar (the variable reference itself)
|
|
470
|
+
c.tsw.WriteLiterally(ident.Name) // Write the identifier name (which holds the variable reference)
|
|
373
471
|
return nil
|
|
374
472
|
}
|
|
375
473
|
}
|
|
376
474
|
|
|
377
|
-
// Otherwise (&
|
|
475
|
+
// Otherwise (&unvarrefedVar, &CompositeLit{}, &FuncCall(), etc.),
|
|
378
476
|
// the address-of operator in Go, when used to create a pointer,
|
|
379
477
|
// translates to simply evaluating the operand in TypeScript.
|
|
380
478
|
// The resulting value (e.g., a new object instance) acts as the "pointer".
|
|
381
|
-
//
|
|
479
|
+
// VarRefing decisions are handled at the assignment site based on the LHS variable.
|
|
382
480
|
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
383
481
|
return fmt.Errorf("failed to write &-operand: %w", err)
|
|
384
482
|
}
|
|
@@ -391,6 +489,13 @@ func (c *GoToTSCompiler) WriteUnaryExpr(exp *ast.UnaryExpr) error {
|
|
|
391
489
|
if !ok {
|
|
392
490
|
return errors.Errorf("unhandled unary op: %s", exp.Op.String())
|
|
393
491
|
}
|
|
492
|
+
|
|
493
|
+
// Special case: In Go, ^ is bitwise NOT when used as unary operator
|
|
494
|
+
// In TypeScript, bitwise NOT is ~, not ^
|
|
495
|
+
if exp.Op == token.XOR {
|
|
496
|
+
tokStr = "~"
|
|
497
|
+
}
|
|
498
|
+
|
|
394
499
|
c.tsw.WriteLiterally(tokStr)
|
|
395
500
|
|
|
396
501
|
// Add space if operator is not postfix (e.g., !)
|
package/compiler/field.go
CHANGED
|
@@ -47,7 +47,7 @@ func (c *GoToTSCompiler) WriteFieldList(a *ast.FieldList, isArguments bool) {
|
|
|
47
47
|
if j > 0 {
|
|
48
48
|
c.tsw.WriteLiterally(", ")
|
|
49
49
|
}
|
|
50
|
-
c.tsw.WriteLiterally(name.Name)
|
|
50
|
+
c.tsw.WriteLiterally(c.sanitizeIdentifier(name.Name))
|
|
51
51
|
c.tsw.WriteLiterally(": ")
|
|
52
52
|
typ := c.pkg.TypesInfo.TypeOf(field.Type)
|
|
53
53
|
c.WriteGoType(typ, GoTypeContextGeneral)
|
|
@@ -65,7 +65,7 @@ func (c *GoToTSCompiler) WriteFieldList(a *ast.FieldList, isArguments bool) {
|
|
|
65
65
|
c.tsw.WriteLiterally(", ")
|
|
66
66
|
}
|
|
67
67
|
c.tsw.WriteLiterally("...")
|
|
68
|
-
c.tsw.WriteLiterally(name.Name)
|
|
68
|
+
c.tsw.WriteLiterally(c.sanitizeIdentifier(name.Name))
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
c.tsw.WriteLiterally(": ")
|
|
@@ -86,7 +86,7 @@ func (c *GoToTSCompiler) WriteFieldList(a *ast.FieldList, isArguments bool) {
|
|
|
86
86
|
if j > 0 {
|
|
87
87
|
c.tsw.WriteLiterally(", ")
|
|
88
88
|
}
|
|
89
|
-
c.tsw.WriteLiterally(name.Name)
|
|
89
|
+
c.tsw.WriteLiterally(c.sanitizeIdentifier(name.Name))
|
|
90
90
|
c.tsw.WriteLiterally(": ")
|
|
91
91
|
typ := c.pkg.TypesInfo.TypeOf(field.Type)
|
|
92
92
|
c.WriteGoType(typ, GoTypeContextGeneral) // Use WriteGoType for parameter type
|
|
@@ -143,7 +143,7 @@ func (c *GoToTSCompiler) WriteField(field *ast.Field, isArguments bool) {
|
|
|
143
143
|
|
|
144
144
|
// argument names: keep original casing, no access modifier
|
|
145
145
|
if isArguments {
|
|
146
|
-
c.tsw.WriteLiterally(name.Name)
|
|
146
|
+
c.tsw.WriteLiterally(c.sanitizeIdentifier(name.Name))
|
|
147
147
|
// Argument type is handled in WriteFieldList, so continue
|
|
148
148
|
continue
|
|
149
149
|
} else {
|
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(",")
|