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
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)
|
package/compiler/stmt-for.go
CHANGED
|
@@ -105,6 +105,17 @@ func (c *GoToTSCompiler) WriteStmtForInit(stmt ast.Stmt) error {
|
|
|
105
105
|
case *ast.ExprStmt:
|
|
106
106
|
// Handle expression statement in init
|
|
107
107
|
return c.WriteValueExpr(s.X)
|
|
108
|
+
case *ast.IncDecStmt:
|
|
109
|
+
// Handle increment/decrement in init (e.g., for i++; ...)
|
|
110
|
+
if err := c.WriteValueExpr(s.X); err != nil { // The expression (e.g., i)
|
|
111
|
+
return err
|
|
112
|
+
}
|
|
113
|
+
tokStr, ok := TokenToTs(s.Tok)
|
|
114
|
+
if !ok {
|
|
115
|
+
return errors.Errorf("unknown incdec token: %v", s.Tok)
|
|
116
|
+
}
|
|
117
|
+
c.tsw.WriteLiterally(tokStr) // The token (e.g., ++)
|
|
118
|
+
return nil
|
|
108
119
|
default:
|
|
109
120
|
return errors.Errorf("unhandled for loop init statement: %T", stmt)
|
|
110
121
|
}
|
package/compiler/stmt-range.go
CHANGED
|
@@ -146,13 +146,15 @@ func (c *GoToTSCompiler) WriteStmtRange(exp *ast.RangeStmt) error {
|
|
|
146
146
|
if err := c.WriteValueExpr(exp.X); err != nil { // This is N
|
|
147
147
|
return fmt.Errorf("failed to write range loop integer expression: %w", err)
|
|
148
148
|
}
|
|
149
|
-
c.tsw.WriteLiterallyf("; %s++) ", indexVarName)
|
|
149
|
+
c.tsw.WriteLiterallyf("; %s++) {", indexVarName)
|
|
150
150
|
|
|
151
151
|
// write body
|
|
152
152
|
if err := c.WriteStmtBlock(exp.Body, false); err != nil {
|
|
153
153
|
return fmt.Errorf("failed to write range loop integer body: %w", err)
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
c.tsw.Indent(-1)
|
|
157
|
+
c.tsw.WriteLine("}")
|
|
156
158
|
return nil
|
|
157
159
|
}
|
|
158
160
|
}
|
|
@@ -231,5 +233,316 @@ func (c *GoToTSCompiler) WriteStmtRange(exp *ast.RangeStmt) error {
|
|
|
231
233
|
}
|
|
232
234
|
}
|
|
233
235
|
|
|
236
|
+
// Handle pointer to array/slice types
|
|
237
|
+
if ptrType, ok := underlying.(*types.Pointer); ok {
|
|
238
|
+
elem := ptrType.Elem().Underlying()
|
|
239
|
+
_, isSlice := elem.(*types.Slice)
|
|
240
|
+
_, isArray := elem.(*types.Array)
|
|
241
|
+
if isArray || isSlice {
|
|
242
|
+
// For pointer to array/slice, we need to dereference the pointer
|
|
243
|
+
// Check if the pointer variable itself is varrefed
|
|
244
|
+
|
|
245
|
+
// Determine the index variable name for the generated loop
|
|
246
|
+
indexVarName := "_i" // Default name
|
|
247
|
+
if exp.Key != nil {
|
|
248
|
+
if keyIdent, ok := exp.Key.(*ast.Ident); ok && keyIdent.Name != "_" {
|
|
249
|
+
indexVarName = keyIdent.Name
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
// If both key and value are provided, use an index loop and assign both
|
|
253
|
+
if exp.Key != nil && exp.Value != nil {
|
|
254
|
+
c.tsw.WriteLiterallyf("for (let %s = 0; %s < $.len(", indexVarName, indexVarName)
|
|
255
|
+
|
|
256
|
+
// Write the pointer expression - use WriteIdent to avoid automatic .value access
|
|
257
|
+
// since we'll add !.value for pointer dereference
|
|
258
|
+
if ident, ok := exp.X.(*ast.Ident); ok {
|
|
259
|
+
c.WriteIdent(ident, false) // Don't add .value here
|
|
260
|
+
} else {
|
|
261
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
262
|
+
return fmt.Errorf("failed to write range loop pointer array/slice expression (key and value): %w", err)
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// Add dereference for the pointer: since we're ranging over a pointer to array/slice,
|
|
266
|
+
// we need to dereference to get to the array/slice
|
|
267
|
+
c.tsw.WriteLiterally("!.value")
|
|
268
|
+
c.tsw.WriteLiterallyf("); %s++) {", indexVarName)
|
|
269
|
+
c.tsw.Indent(1)
|
|
270
|
+
c.tsw.WriteLine("")
|
|
271
|
+
// Declare value if not blank
|
|
272
|
+
if ident, ok := exp.Value.(*ast.Ident); ok && ident.Name != "_" {
|
|
273
|
+
c.tsw.WriteLiterally("const ")
|
|
274
|
+
c.WriteIdent(ident, false)
|
|
275
|
+
c.tsw.WriteLiterally(" = ")
|
|
276
|
+
// Write the pointer expression again for value access
|
|
277
|
+
if identX, ok := exp.X.(*ast.Ident); ok {
|
|
278
|
+
c.WriteIdent(identX, false) // Don't add .value here
|
|
279
|
+
} else {
|
|
280
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
281
|
+
return fmt.Errorf("failed to write range loop pointer array/slice value expression: %w", err)
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
c.tsw.WriteLiterally("!.value![")
|
|
285
|
+
c.tsw.WriteLiterally(indexVarName)
|
|
286
|
+
c.tsw.WriteLiterally("]")
|
|
287
|
+
c.tsw.WriteLine("")
|
|
288
|
+
}
|
|
289
|
+
if err := c.WriteStmt(exp.Body); err != nil {
|
|
290
|
+
return fmt.Errorf("failed to write range loop pointer array/slice body (key and value): %w", err)
|
|
291
|
+
}
|
|
292
|
+
c.tsw.Indent(-1)
|
|
293
|
+
c.tsw.WriteLine("}")
|
|
294
|
+
return nil
|
|
295
|
+
} else if exp.Key != nil && exp.Value == nil { // Only key provided
|
|
296
|
+
c.tsw.WriteLiterallyf("for (let %s = 0; %s < $.len(", indexVarName, indexVarName)
|
|
297
|
+
// Write the pointer expression - use WriteIdent to avoid automatic .value access
|
|
298
|
+
// since we'll add !.value for pointer dereference
|
|
299
|
+
if ident, ok := exp.X.(*ast.Ident); ok {
|
|
300
|
+
c.WriteIdent(ident, false) // Don't add .value here
|
|
301
|
+
} else {
|
|
302
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
303
|
+
return fmt.Errorf("failed to write expression for the pointer iterable: %w", err)
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
c.tsw.WriteLiterally("!.value")
|
|
307
|
+
c.tsw.WriteLiterallyf("); %s++) {", indexVarName)
|
|
308
|
+
c.tsw.Indent(1)
|
|
309
|
+
c.tsw.WriteLine("")
|
|
310
|
+
if err := c.WriteStmtBlock(exp.Body, false); err != nil {
|
|
311
|
+
return fmt.Errorf("failed to write range loop pointer array/slice body (only key): %w", err)
|
|
312
|
+
}
|
|
313
|
+
c.tsw.Indent(-1)
|
|
314
|
+
c.tsw.WriteLine("}")
|
|
315
|
+
return nil
|
|
316
|
+
} else if exp.Key == nil && exp.Value != nil { // Only value provided
|
|
317
|
+
// I think this is impossible. See for_range_value_only test.
|
|
318
|
+
return errors.Errorf("unexpected value without key in for range expression: %v", exp)
|
|
319
|
+
} else {
|
|
320
|
+
// Fallback: simple index loop without declaring range variables, use _i
|
|
321
|
+
indexVarName := "_i"
|
|
322
|
+
c.tsw.WriteLiterallyf("for (let %s = 0; %s < $.len(", indexVarName, indexVarName)
|
|
323
|
+
// Write the pointer expression - use WriteIdent to avoid automatic .value access
|
|
324
|
+
// since we'll add !.value for pointer dereference
|
|
325
|
+
if ident, ok := exp.X.(*ast.Ident); ok {
|
|
326
|
+
c.WriteIdent(ident, false) // Don't add .value here
|
|
327
|
+
} else {
|
|
328
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
329
|
+
return fmt.Errorf("failed to write range loop pointer array/slice length expression (fallback): %w", err)
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
c.tsw.WriteLiterally("!.value")
|
|
333
|
+
c.tsw.WriteLiterallyf("); %s++) {", indexVarName)
|
|
334
|
+
c.tsw.Indent(1)
|
|
335
|
+
c.tsw.WriteLine("")
|
|
336
|
+
if err := c.WriteStmtBlock(exp.Body, false); err != nil {
|
|
337
|
+
return fmt.Errorf("failed to write range loop pointer array/slice body (fallback): %w", err)
|
|
338
|
+
}
|
|
339
|
+
c.tsw.Indent(-1)
|
|
340
|
+
c.tsw.WriteLine("}")
|
|
341
|
+
return nil
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Handle iterator function signatures
|
|
347
|
+
if sig, ok := underlying.(*types.Signature); ok {
|
|
348
|
+
// Check if this is an iterator function signature
|
|
349
|
+
// Iterator functions have the form: func(yield func(...) bool)
|
|
350
|
+
params := sig.Params()
|
|
351
|
+
if params.Len() == 1 {
|
|
352
|
+
yieldParam := params.At(0).Type()
|
|
353
|
+
if yieldSig, ok := yieldParam.Underlying().(*types.Signature); ok {
|
|
354
|
+
yieldParams := yieldSig.Params()
|
|
355
|
+
yieldResults := yieldSig.Results()
|
|
356
|
+
|
|
357
|
+
// Verify the yield function returns bool
|
|
358
|
+
if yieldResults.Len() == 1 {
|
|
359
|
+
if basic, ok := yieldResults.At(0).Type().Underlying().(*types.Basic); ok && basic.Kind() == types.Bool {
|
|
360
|
+
// This is an iterator function
|
|
361
|
+
// Generate TypeScript code that calls the iterator with a yield function
|
|
362
|
+
|
|
363
|
+
if yieldParams.Len() == 0 {
|
|
364
|
+
// func(func() bool) - iterator with no values
|
|
365
|
+
c.tsw.WriteLiterally(";(() => {")
|
|
366
|
+
c.tsw.Indent(1)
|
|
367
|
+
c.tsw.WriteLine("")
|
|
368
|
+
c.tsw.WriteLiterally("let shouldContinue = true")
|
|
369
|
+
c.tsw.WriteLine("")
|
|
370
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
371
|
+
return fmt.Errorf("failed to write iterator expression: %w", err)
|
|
372
|
+
}
|
|
373
|
+
c.tsw.WriteLiterally("(() => {")
|
|
374
|
+
c.tsw.Indent(1)
|
|
375
|
+
c.tsw.WriteLine("")
|
|
376
|
+
if err := c.WriteStmtBlock(exp.Body, false); err != nil {
|
|
377
|
+
return fmt.Errorf("failed to write iterator body: %w", err)
|
|
378
|
+
}
|
|
379
|
+
c.tsw.WriteLiterally("return shouldContinue")
|
|
380
|
+
c.tsw.WriteLine("")
|
|
381
|
+
c.tsw.Indent(-1)
|
|
382
|
+
c.tsw.WriteLiterally("})")
|
|
383
|
+
c.tsw.WriteLine("")
|
|
384
|
+
c.tsw.Indent(-1)
|
|
385
|
+
c.tsw.WriteLine("})()")
|
|
386
|
+
return nil
|
|
387
|
+
} else if yieldParams.Len() == 1 {
|
|
388
|
+
// func(func(V) bool) - iterator with one value
|
|
389
|
+
c.tsw.WriteLiterally(";(() => {")
|
|
390
|
+
c.tsw.Indent(1)
|
|
391
|
+
c.tsw.WriteLine("")
|
|
392
|
+
c.tsw.WriteLiterally("let shouldContinue = true")
|
|
393
|
+
c.tsw.WriteLine("")
|
|
394
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
395
|
+
return fmt.Errorf("failed to write iterator expression: %w", err)
|
|
396
|
+
}
|
|
397
|
+
c.tsw.WriteLiterally("((")
|
|
398
|
+
if exp.Value != nil {
|
|
399
|
+
if ident, ok := exp.Value.(*ast.Ident); ok && ident.Name != "_" {
|
|
400
|
+
c.WriteIdent(ident, false)
|
|
401
|
+
} else {
|
|
402
|
+
c.tsw.WriteLiterally("v")
|
|
403
|
+
}
|
|
404
|
+
} else {
|
|
405
|
+
c.tsw.WriteLiterally("v")
|
|
406
|
+
}
|
|
407
|
+
c.tsw.WriteLiterally(") => {")
|
|
408
|
+
c.tsw.Indent(1)
|
|
409
|
+
c.tsw.WriteLine("")
|
|
410
|
+
if err := c.WriteStmtBlock(exp.Body, false); err != nil {
|
|
411
|
+
return fmt.Errorf("failed to write iterator body: %w", err)
|
|
412
|
+
}
|
|
413
|
+
c.tsw.WriteLiterally("return shouldContinue")
|
|
414
|
+
c.tsw.WriteLine("")
|
|
415
|
+
c.tsw.Indent(-1)
|
|
416
|
+
c.tsw.WriteLiterally("})")
|
|
417
|
+
c.tsw.WriteLine("")
|
|
418
|
+
c.tsw.Indent(-1)
|
|
419
|
+
c.tsw.WriteLine("})()")
|
|
420
|
+
return nil
|
|
421
|
+
} else if yieldParams.Len() == 2 {
|
|
422
|
+
// func(func(K, V) bool) - iterator with key-value pairs
|
|
423
|
+
c.tsw.WriteLiterally(";(() => {")
|
|
424
|
+
c.tsw.Indent(1)
|
|
425
|
+
c.tsw.WriteLine("")
|
|
426
|
+
c.tsw.WriteLiterally("let shouldContinue = true")
|
|
427
|
+
c.tsw.WriteLine("")
|
|
428
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
429
|
+
return fmt.Errorf("failed to write iterator expression: %w", err)
|
|
430
|
+
}
|
|
431
|
+
c.tsw.WriteLiterally("((")
|
|
432
|
+
if exp.Key != nil {
|
|
433
|
+
if ident, ok := exp.Key.(*ast.Ident); ok && ident.Name != "_" {
|
|
434
|
+
c.WriteIdent(ident, false)
|
|
435
|
+
} else {
|
|
436
|
+
c.tsw.WriteLiterally("k")
|
|
437
|
+
}
|
|
438
|
+
} else {
|
|
439
|
+
c.tsw.WriteLiterally("k")
|
|
440
|
+
}
|
|
441
|
+
c.tsw.WriteLiterally(", ")
|
|
442
|
+
if exp.Value != nil {
|
|
443
|
+
if ident, ok := exp.Value.(*ast.Ident); ok && ident.Name != "_" {
|
|
444
|
+
c.WriteIdent(ident, false)
|
|
445
|
+
} else {
|
|
446
|
+
c.tsw.WriteLiterally("v")
|
|
447
|
+
}
|
|
448
|
+
} else {
|
|
449
|
+
c.tsw.WriteLiterally("v")
|
|
450
|
+
}
|
|
451
|
+
c.tsw.WriteLiterally(") => {")
|
|
452
|
+
c.tsw.Indent(1)
|
|
453
|
+
c.tsw.WriteLine("")
|
|
454
|
+
if err := c.WriteStmtBlock(exp.Body, false); err != nil {
|
|
455
|
+
return fmt.Errorf("failed to write iterator body: %w", err)
|
|
456
|
+
}
|
|
457
|
+
c.tsw.WriteLiterally("return shouldContinue")
|
|
458
|
+
c.tsw.WriteLine("")
|
|
459
|
+
c.tsw.Indent(-1)
|
|
460
|
+
c.tsw.WriteLiterally("})")
|
|
461
|
+
c.tsw.WriteLine("")
|
|
462
|
+
c.tsw.Indent(-1)
|
|
463
|
+
c.tsw.WriteLine("})()")
|
|
464
|
+
return nil
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Handle interface types that may represent iterators
|
|
473
|
+
if _, ok := underlying.(*types.Interface); ok {
|
|
474
|
+
// For interface types, we need to treat them as potential iterators
|
|
475
|
+
// In Go 1.23+, interfaces can represent iterator functions
|
|
476
|
+
// We'll attempt to call them as iterator functions with a yield callback
|
|
477
|
+
|
|
478
|
+
// Try to determine the iterator pattern based on context or assume key-value pairs
|
|
479
|
+
// Since we can't easily determine the exact signature from just the interface,
|
|
480
|
+
// we'll generate a generic iterator call pattern
|
|
481
|
+
|
|
482
|
+
c.tsw.WriteLiterally(";(() => {")
|
|
483
|
+
c.tsw.Indent(1)
|
|
484
|
+
c.tsw.WriteLine("")
|
|
485
|
+
c.tsw.WriteLiterally("let shouldContinue = true")
|
|
486
|
+
c.tsw.WriteLine("")
|
|
487
|
+
|
|
488
|
+
// Call the interface as an iterator function
|
|
489
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
490
|
+
return fmt.Errorf("failed to write interface iterator expression: %w", err)
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Generate the appropriate yield function based on the range variables
|
|
494
|
+
if exp.Key != nil && exp.Value != nil {
|
|
495
|
+
// Key-value iterator
|
|
496
|
+
c.tsw.WriteLiterally("((")
|
|
497
|
+
if ident, ok := exp.Key.(*ast.Ident); ok && ident.Name != "_" {
|
|
498
|
+
c.WriteIdent(ident, false)
|
|
499
|
+
} else {
|
|
500
|
+
c.tsw.WriteLiterally("k")
|
|
501
|
+
}
|
|
502
|
+
c.tsw.WriteLiterally(", ")
|
|
503
|
+
if ident, ok := exp.Value.(*ast.Ident); ok && ident.Name != "_" {
|
|
504
|
+
c.WriteIdent(ident, false)
|
|
505
|
+
} else {
|
|
506
|
+
c.tsw.WriteLiterally("v")
|
|
507
|
+
}
|
|
508
|
+
c.tsw.WriteLiterally(") => {")
|
|
509
|
+
} else if exp.Value != nil {
|
|
510
|
+
// Value-only iterator
|
|
511
|
+
c.tsw.WriteLiterally("((")
|
|
512
|
+
if ident, ok := exp.Value.(*ast.Ident); ok && ident.Name != "_" {
|
|
513
|
+
c.WriteIdent(ident, false)
|
|
514
|
+
} else {
|
|
515
|
+
c.tsw.WriteLiterally("v")
|
|
516
|
+
}
|
|
517
|
+
c.tsw.WriteLiterally(") => {")
|
|
518
|
+
} else if exp.Key != nil {
|
|
519
|
+
// Key-only iterator (treating it as value for single-param iterator)
|
|
520
|
+
c.tsw.WriteLiterally("((")
|
|
521
|
+
if ident, ok := exp.Key.(*ast.Ident); ok && ident.Name != "_" {
|
|
522
|
+
c.WriteIdent(ident, false)
|
|
523
|
+
} else {
|
|
524
|
+
c.tsw.WriteLiterally("k")
|
|
525
|
+
}
|
|
526
|
+
c.tsw.WriteLiterally(") => {")
|
|
527
|
+
} else {
|
|
528
|
+
// No variables iterator
|
|
529
|
+
c.tsw.WriteLiterally("(() => {")
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
c.tsw.Indent(1)
|
|
533
|
+
c.tsw.WriteLine("")
|
|
534
|
+
if err := c.WriteStmtBlock(exp.Body, false); err != nil {
|
|
535
|
+
return fmt.Errorf("failed to write interface iterator body: %w", err)
|
|
536
|
+
}
|
|
537
|
+
c.tsw.WriteLiterally("return shouldContinue")
|
|
538
|
+
c.tsw.WriteLine("")
|
|
539
|
+
c.tsw.Indent(-1)
|
|
540
|
+
c.tsw.WriteLiterally("})")
|
|
541
|
+
c.tsw.WriteLine("")
|
|
542
|
+
c.tsw.Indent(-1)
|
|
543
|
+
c.tsw.WriteLine("})()")
|
|
544
|
+
return nil
|
|
545
|
+
}
|
|
546
|
+
|
|
234
547
|
return errors.Errorf("unsupported range loop type: %T for expression %v", underlying, exp)
|
|
235
548
|
}
|