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.
Files changed (94) hide show
  1. package/README.md +2 -2
  2. package/cmd/goscript/cmd_compile.go +18 -2
  3. package/compiler/analysis.go +74 -132
  4. package/compiler/analysis_test.go +220 -0
  5. package/compiler/assignment.go +37 -43
  6. package/compiler/builtin_test.go +90 -0
  7. package/compiler/compiler.go +307 -22
  8. package/compiler/composite-lit.go +108 -43
  9. package/compiler/config.go +7 -3
  10. package/compiler/config_test.go +6 -33
  11. package/compiler/decl.go +7 -1
  12. package/compiler/expr-call.go +212 -2
  13. package/compiler/expr-selector.go +66 -41
  14. package/compiler/expr-star.go +57 -65
  15. package/compiler/expr-type.go +1 -1
  16. package/compiler/expr-value.go +1 -1
  17. package/compiler/expr.go +125 -20
  18. package/compiler/field.go +4 -4
  19. package/compiler/primitive.go +11 -10
  20. package/compiler/spec-struct.go +3 -3
  21. package/compiler/spec-value.go +75 -29
  22. package/compiler/spec.go +9 -3
  23. package/compiler/stmt-assign.go +36 -2
  24. package/compiler/stmt-for.go +11 -0
  25. package/compiler/stmt-range.go +314 -1
  26. package/compiler/stmt.go +52 -0
  27. package/compiler/type.go +83 -15
  28. package/dist/gs/builtin/builtin.d.ts +9 -0
  29. package/dist/gs/builtin/builtin.js +46 -0
  30. package/dist/gs/builtin/builtin.js.map +1 -0
  31. package/dist/gs/builtin/channel.d.ts +193 -0
  32. package/dist/gs/builtin/channel.js +471 -0
  33. package/dist/gs/builtin/channel.js.map +1 -0
  34. package/dist/gs/builtin/defer.d.ts +38 -0
  35. package/dist/gs/builtin/defer.js +54 -0
  36. package/dist/gs/builtin/defer.js.map +1 -0
  37. package/dist/gs/builtin/index.d.ts +1 -0
  38. package/dist/gs/builtin/index.js +2 -0
  39. package/dist/gs/builtin/index.js.map +1 -0
  40. package/dist/gs/builtin/io.d.ts +16 -0
  41. package/dist/gs/builtin/io.js +15 -0
  42. package/dist/gs/builtin/io.js.map +1 -0
  43. package/dist/gs/builtin/map.d.ts +33 -0
  44. package/dist/gs/builtin/map.js +44 -0
  45. package/dist/gs/builtin/map.js.map +1 -0
  46. package/dist/gs/builtin/slice.d.ts +173 -0
  47. package/dist/gs/builtin/slice.js +799 -0
  48. package/dist/gs/builtin/slice.js.map +1 -0
  49. package/dist/gs/builtin/type.d.ts +203 -0
  50. package/dist/gs/builtin/type.js +744 -0
  51. package/dist/gs/builtin/type.js.map +1 -0
  52. package/dist/gs/builtin/varRef.d.ts +14 -0
  53. package/dist/gs/builtin/varRef.js +14 -0
  54. package/dist/gs/builtin/varRef.js.map +1 -0
  55. package/dist/gs/cmp/index.d.ts +4 -0
  56. package/dist/gs/cmp/index.js +27 -0
  57. package/dist/gs/cmp/index.js.map +1 -0
  58. package/dist/gs/context/context.d.ts +26 -0
  59. package/dist/gs/context/context.js +305 -0
  60. package/dist/gs/context/context.js.map +1 -0
  61. package/dist/gs/context/index.d.ts +1 -0
  62. package/dist/gs/context/index.js +2 -0
  63. package/dist/gs/context/index.js.map +1 -0
  64. package/dist/gs/internal/goarch/index.d.ts +6 -0
  65. package/dist/gs/internal/goarch/index.js +14 -0
  66. package/dist/gs/internal/goarch/index.js.map +1 -0
  67. package/dist/gs/iter/index.d.ts +1 -0
  68. package/dist/gs/iter/index.js +2 -0
  69. package/dist/gs/iter/index.js.map +1 -0
  70. package/dist/gs/iter/iter.d.ts +4 -0
  71. package/dist/gs/iter/iter.js +91 -0
  72. package/dist/gs/iter/iter.js.map +1 -0
  73. package/dist/gs/math/bits/index.d.ts +47 -0
  74. package/dist/gs/math/bits/index.js +298 -0
  75. package/dist/gs/math/bits/index.js.map +1 -0
  76. package/dist/gs/runtime/index.d.ts +1 -0
  77. package/dist/gs/runtime/index.js +2 -0
  78. package/dist/gs/runtime/index.js.map +1 -0
  79. package/dist/gs/runtime/runtime.d.ts +41 -0
  80. package/dist/gs/runtime/runtime.js +158 -0
  81. package/dist/gs/runtime/runtime.js.map +1 -0
  82. package/dist/gs/slices/index.d.ts +1 -0
  83. package/dist/gs/slices/index.js +2 -0
  84. package/dist/gs/slices/index.js.map +1 -0
  85. package/dist/gs/slices/slices.d.ts +8 -0
  86. package/dist/gs/slices/slices.js +20 -0
  87. package/dist/gs/slices/slices.js.map +1 -0
  88. package/dist/gs/time/index.d.ts +1 -0
  89. package/dist/gs/time/index.js +2 -0
  90. package/dist/gs/time/index.js.map +1 -0
  91. package/dist/gs/time/time.d.ts +57 -0
  92. package/dist/gs/time/time.js +208 -0
  93. package/dist/gs/time/time.js.map +1 -0
  94. package/package.json +3 -2
@@ -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 boxed (e.g., if its address is taken)
16
- // using `c.analysis.NeedsBoxed(obj)`.
17
- // - If boxed: `let x: $.Box<T_ts> = $.box(initializer_ts_or_zero_ts);`
18
- // The type annotation is `$.Box<T_ts>`, and the initializer is wrapped in `$.box()`.
19
- // - If not boxed: `let x: T_ts = initializer_ts_or_zero_ts;`
20
- // The type annotation is `T_ts`. If the initializer is `&unboxedVar`, it becomes `$.box(unboxedVar_ts)`.
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
- needsBox := c.analysis.NeedsBoxed(obj) // Check if address is taken
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
- if !isSliceConversion {
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 needsBox {
93
- // If boxed, the variable holds Box<OriginalGoType>
94
- c.tsw.WriteLiterally("$.Box<")
95
- c.WriteGoType(goType, GoTypeContextGeneral) // Write the original Go type T
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 boxed, the variable holds the translated Go type directly
99
- c.WriteGoType(goType, GoTypeContextGeneral)
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 needsBox {
125
- // Boxed variable: let v: Box<T> = $.box(init_or_zero);
126
- c.tsw.WriteLiterally("$.box(")
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, box the zero value
179
+ // No initializer, varRef the zero value
134
180
  c.WriteZeroValueForType(goType)
135
181
  }
136
182
  c.tsw.WriteLiterally(")")
137
183
  } else {
138
- // Unboxed variable: let v: T = init_or_zero;
184
+ // Unvarrefed variable: let v: T = init_or_zero;
139
185
  if hasInitializer {
140
- // Handle &v initializer specifically for unboxed variables
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 boxed
144
- needsBoxOperand := false
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
- needsBoxOperand = innerObj != nil && c.analysis.NeedsBoxed(innerObj)
194
+ needsVarRefOperand = innerObj != nil && c.analysis.NeedsVarRef(innerObj)
149
195
  }
150
196
 
151
- // If v is boxed, assign the box itself (v)
152
- // If v is not boxed, assign $.box(v)
153
- if needsBoxOperand {
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
- // &unboxedVar -> $.box(unboxedVar)
158
- c.tsw.WriteLiterally("$.box(")
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) writeBoxedFieldInitializer(fieldName string, fieldType types.Type, isEmbedded bool) {
81
+ func (c *GoToTSCompiler) writeVarRefedFieldInitializer(fieldName string, fieldType types.Type, isEmbedded bool) {
82
82
  c.tsw.WriteLiterally(fieldName)
83
- c.tsw.WriteLiterally(": $.box(")
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(": $.box(")
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() }`,
@@ -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 boxed, `let x: $.Box<T> = $.box(y_ts);`.
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 boxing and
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)
@@ -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
  }
@@ -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
  }