goscript 0.0.33 → 0.0.34

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 (42) hide show
  1. package/compiler/analysis.go +2 -2
  2. package/compiler/assignment.go +26 -0
  3. package/compiler/builtin_test.go +2 -0
  4. package/compiler/compiler.go +12 -2
  5. package/compiler/compiler_test.go +0 -53
  6. package/compiler/expr-call.go +121 -2
  7. package/compiler/expr.go +66 -1
  8. package/compiler/lit.go +1 -1
  9. package/compiler/stmt-assign.go +106 -90
  10. package/compiler/stmt-for.go +78 -1
  11. package/compiler/stmt-range.go +333 -461
  12. package/compiler/stmt.go +20 -0
  13. package/compiler/type.go +11 -8
  14. package/dist/gs/builtin/builtin.d.ts +7 -0
  15. package/dist/gs/builtin/builtin.js +30 -0
  16. package/dist/gs/builtin/builtin.js.map +1 -1
  17. package/dist/gs/builtin/map.d.ts +4 -4
  18. package/dist/gs/builtin/map.js +12 -6
  19. package/dist/gs/builtin/map.js.map +1 -1
  20. package/dist/gs/builtin/slice.d.ts +7 -7
  21. package/dist/gs/builtin/slice.js +19 -9
  22. package/dist/gs/builtin/slice.js.map +1 -1
  23. package/dist/gs/maps/index.d.ts +2 -0
  24. package/dist/gs/maps/index.js +3 -0
  25. package/dist/gs/maps/index.js.map +1 -0
  26. package/dist/gs/maps/iter.gs.d.ts +7 -0
  27. package/dist/gs/maps/iter.gs.js +65 -0
  28. package/dist/gs/maps/iter.gs.js.map +1 -0
  29. package/dist/gs/maps/maps.gs.d.ts +7 -0
  30. package/dist/gs/maps/maps.gs.js +79 -0
  31. package/dist/gs/maps/maps.gs.js.map +1 -0
  32. package/dist/gs/slices/slices.d.ts +6 -0
  33. package/dist/gs/slices/slices.js +8 -0
  34. package/dist/gs/slices/slices.js.map +1 -1
  35. package/gs/builtin/builtin.ts +38 -0
  36. package/gs/builtin/map.ts +10 -9
  37. package/gs/builtin/slice.ts +23 -11
  38. package/gs/maps/index.ts +2 -0
  39. package/gs/maps/iter.gs.ts +71 -0
  40. package/gs/maps/maps.gs.ts +87 -0
  41. package/gs/slices/slices.ts +9 -0
  42. package/package.json +1 -1
@@ -46,503 +46,375 @@ func (c *GoToTSCompiler) WriteStmtRange(exp *ast.RangeStmt) error {
46
46
  iterType := c.pkg.TypesInfo.TypeOf(exp.X)
47
47
  underlying := iterType.Underlying()
48
48
 
49
- // Handle map types
50
- if _, ok := underlying.(*types.Map); ok {
51
- // Use for-of with entries() for proper Map iteration
52
- c.tsw.WriteLiterally("for (const [k, v] of ")
53
- if err := c.WriteValueExpr(exp.X); err != nil {
54
- return fmt.Errorf("failed to write range loop map expression: %w", err)
55
- }
56
- c.tsw.WriteLiterally(".entries()) {")
57
- c.tsw.Indent(1)
58
- c.tsw.WriteLine("")
59
- // If a key variable is provided and is not blank, declare it as a constant
60
- if exp.Key != nil {
61
- if ident, ok := exp.Key.(*ast.Ident); ok && ident.Name != "_" {
62
- c.tsw.WriteLiterally("const ")
63
- c.WriteIdent(ident, false)
64
- c.tsw.WriteLiterally(" = k")
65
- c.tsw.WriteLine("")
66
- }
67
- }
68
- // If a value variable is provided and is not blank, use the value from entries()
69
- if exp.Value != nil {
70
- if ident, ok := exp.Value.(*ast.Ident); ok && ident.Name != "_" {
71
- c.tsw.WriteLiterally("const ")
72
- c.WriteIdent(ident, false)
73
- c.tsw.WriteLiterally(" = v")
74
- c.tsw.WriteLine("")
75
- }
76
- }
77
- // Write the loop body
78
- if err := c.WriteStmtBlock(exp.Body, false); err != nil {
79
- return fmt.Errorf("failed to write range loop map body: %w", err)
80
- }
81
- c.tsw.Indent(-1)
82
- c.tsw.WriteLine("}")
83
- return nil
49
+ // Handle map types (both concrete maps and type parameters constrained to maps)
50
+ if c.isMapType(iterType, underlying) {
51
+ return c.writeMapRange(exp)
84
52
  }
85
53
 
86
54
  // Handle basic types (string, integer)
87
55
  if basic, ok := underlying.(*types.Basic); ok {
88
56
  if basic.Info()&types.IsString != 0 {
89
- // Add a scope to avoid collision of _runes variable
90
- c.tsw.WriteLine("{")
91
- c.tsw.Indent(1)
92
-
93
- // Convert the string to runes using $.stringToRunes
94
- c.tsw.WriteLiterally("const _runes = $.stringToRunes(")
95
- if err := c.WriteValueExpr(exp.X); err != nil {
96
- return fmt.Errorf("failed to write range loop string conversion expression: %w", err)
97
- }
98
- c.tsw.WriteLiterally(")")
99
- c.tsw.WriteLine("")
100
-
101
- // Determine the index variable name for the generated loop
102
- indexVarName := "i" // Default name
103
- if exp.Key != nil {
104
- if keyIdent, ok := exp.Key.(*ast.Ident); ok && keyIdent.Name != "_" {
105
- indexVarName = keyIdent.Name
106
- }
107
- }
108
- c.tsw.WriteLiterallyf("for (let %s = 0; %s < _runes.length; %s++) {", indexVarName, indexVarName, indexVarName)
109
- c.tsw.Indent(1)
110
- c.tsw.WriteLine("")
111
- // Declare value if provided and not blank
112
- if exp.Value != nil {
113
- if ident, ok := exp.Value.(*ast.Ident); ok && ident.Name != "_" {
114
- c.tsw.WriteLiterally("const ")
115
- c.WriteIdent(ident, false)
116
- c.tsw.WriteLiterally(" = _runes[i]") // TODO: should be indexVarName?
117
- c.tsw.WriteLine("")
118
- }
119
- }
120
- if err := c.WriteStmtBlock(exp.Body, false); err != nil {
121
- return fmt.Errorf("failed to write range loop string body: %w", err)
122
- }
123
- c.tsw.Indent(-1)
124
- c.tsw.WriteLine("}")
125
-
126
- // outer }
127
- c.tsw.Indent(-1)
128
- c.tsw.WriteLine("}")
129
- return nil
57
+ return c.writeStringRange(exp)
130
58
  } else if basic.Info()&types.IsInteger != 0 {
131
- // The value variable is not allowed ranging over an integer.
132
- if exp.Value != nil {
133
- return errors.Errorf("ranging over an integer supports key variable only (not value variable): %v", exp)
134
- }
135
-
136
- // Handle ranging over an integer (Go 1.22+)
137
- // Determine the index variable name for the generated loop
138
- indexVarName := "_i" // Default name
139
- if exp.Key != nil {
140
- if keyIdent, ok := exp.Key.(*ast.Ident); ok && keyIdent.Name != "_" {
141
- indexVarName = keyIdent.Name
142
- }
143
- }
144
-
145
- c.tsw.WriteLiterallyf("for (let %s = 0; %s < ", indexVarName, indexVarName)
146
- if err := c.WriteValueExpr(exp.X); err != nil { // This is N
147
- return fmt.Errorf("failed to write range loop integer expression: %w", err)
148
- }
149
- c.tsw.WriteLiterallyf("; %s++) {", indexVarName)
150
-
151
- // write body
152
- if err := c.WriteStmtBlock(exp.Body, false); err != nil {
153
- return fmt.Errorf("failed to write range loop integer body: %w", err)
154
- }
155
-
156
- c.tsw.Indent(-1)
157
- c.tsw.WriteLine("}")
158
- return nil
59
+ return c.writeIntegerRange(exp)
159
60
  }
160
61
  }
161
62
 
162
63
  // Handle array and slice types
163
- _, isSlice := underlying.(*types.Slice)
164
- _, isArray := underlying.(*types.Array)
165
- if isArray || isSlice {
166
- // Determine the index variable name for the generated loop
167
- indexVarName := "_i" // Default name
168
- if exp.Key != nil {
169
- if keyIdent, ok := exp.Key.(*ast.Ident); ok && keyIdent.Name != "_" {
170
- indexVarName = keyIdent.Name
171
- }
172
- }
173
- // If both key and value are provided, use an index loop and assign both
174
- if exp.Key != nil && exp.Value != nil {
175
- c.tsw.WriteLiterallyf("for (let %s = 0; %s < $.len(", indexVarName, indexVarName)
176
- if err := c.WriteValueExpr(exp.X); err != nil { // Write the expression for the iterable
177
- return fmt.Errorf("failed to write range loop array/slice expression (key and value): %w", err)
178
- }
179
- c.tsw.WriteLiterallyf("); %s++) {", indexVarName)
180
- c.tsw.Indent(1)
181
- c.tsw.WriteLine("")
182
- // Declare value if not blank
183
- if ident, ok := exp.Value.(*ast.Ident); ok && ident.Name != "_" {
184
- c.tsw.WriteLiterally("const ")
185
- c.WriteIdent(ident, false)
186
- c.tsw.WriteLiterally(" = ")
187
- if err := c.WriteValueExpr(exp.X); err != nil {
188
- return fmt.Errorf("failed to write range loop array/slice value expression: %w", err)
189
- }
190
- c.tsw.WriteLiterallyf("![%s]", indexVarName) // Use indexVarName with not-null assert
191
- c.tsw.WriteLine("")
192
- }
193
- if err := c.WriteStmt(exp.Body); err != nil {
194
- return fmt.Errorf("failed to write range loop array/slice body (key and value): %w", err)
195
- }
196
- c.tsw.Indent(-1)
197
- c.tsw.WriteLine("}")
198
- return nil
199
- } else if exp.Key != nil && exp.Value == nil { // Only key provided
200
- c.tsw.WriteLiterallyf("for (let %s = 0; %s < $.len(", indexVarName, indexVarName)
201
- // Write the expression for the iterable
202
- if err := c.WriteValueExpr(exp.X); err != nil {
203
- return fmt.Errorf("failed to write expression for the iterable: %w", err)
204
- }
205
- c.tsw.WriteLiterallyf("); %s++) {", indexVarName)
206
- c.tsw.Indent(1)
207
- c.tsw.WriteLine("")
208
- if err := c.WriteStmtBlock(exp.Body, false); err != nil {
209
- return fmt.Errorf("failed to write range loop array/slice body (only key): %w", err)
210
- }
211
- c.tsw.Indent(-1)
212
- c.tsw.WriteLine("}")
213
- return nil
214
- } else if exp.Key == nil && exp.Value != nil { // Only value provided
215
- // I think this is impossible. See for_range_value_only test.
216
- return errors.Errorf("unexpected value without key in for range expression: %v", exp)
217
- } else {
218
- // Fallback: simple index loop without declaring range variables, use _i
219
- indexVarName := "_i"
220
- c.tsw.WriteLiterallyf("for (let %s = 0; %s < $.len(", indexVarName, indexVarName)
221
- if err := c.WriteValueExpr(exp.X); err != nil {
222
- return fmt.Errorf("failed to write range loop array/slice length expression (fallback): %w", err)
223
- }
224
- c.tsw.WriteLiterallyf("); %s++) {", indexVarName)
225
- c.tsw.Indent(1)
226
- c.tsw.WriteLine("")
227
- if err := c.WriteStmtBlock(exp.Body, false); err != nil {
228
- return fmt.Errorf("failed to write range loop array/slice body (fallback): %w", err)
229
- }
230
- c.tsw.Indent(-1)
231
- c.tsw.WriteLine("}")
232
- return nil
233
- }
64
+ if c.isArrayOrSlice(underlying) {
65
+ return c.writeArraySliceRange(exp, false)
234
66
  }
235
67
 
236
68
  // Handle pointer to array/slice types
237
69
  if ptrType, ok := underlying.(*types.Pointer); ok {
238
70
  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
- }
71
+ if c.isArrayOrSlice(elem) {
72
+ return c.writeArraySliceRange(exp, true)
343
73
  }
344
74
  }
345
75
 
346
76
  // Handle iterator function signatures
347
77
  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
- }
78
+ if c.isIteratorSignature(sig) {
79
+ return c.writeIteratorRange(exp, sig)
469
80
  }
470
81
  }
471
82
 
472
83
  // Handle interface types that may represent iterators
473
84
  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
85
+ return c.writeInterfaceIteratorRange(exp)
86
+ }
477
87
 
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
88
+ return errors.Errorf("unsupported range loop type: %T for expression %v", underlying, exp)
89
+ }
481
90
 
482
- c.tsw.WriteLiterally(";(() => {")
483
- c.tsw.Indent(1)
484
- c.tsw.WriteLine("")
485
- c.tsw.WriteLiterally("let shouldContinue = true")
486
- c.tsw.WriteLine("")
91
+ // Helper functions
487
92
 
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)
93
+ func (c *GoToTSCompiler) isMapType(iterType, underlying types.Type) bool {
94
+ if _, ok := underlying.(*types.Map); ok {
95
+ return true
96
+ }
97
+ if typeParam, isTypeParam := iterType.(*types.TypeParam); isTypeParam {
98
+ constraint := typeParam.Constraint()
99
+ if constraint != nil {
100
+ constraintUnderlying := constraint.Underlying()
101
+ if iface, isInterface := constraintUnderlying.(*types.Interface); isInterface {
102
+ return hasMapConstraint(iface)
103
+ }
491
104
  }
105
+ }
106
+ return false
107
+ }
492
108
 
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")
109
+ func (c *GoToTSCompiler) isArrayOrSlice(underlying types.Type) bool {
110
+ _, isSlice := underlying.(*types.Slice)
111
+ _, isArray := underlying.(*types.Array)
112
+ return isArray || isSlice
113
+ }
114
+
115
+ func (c *GoToTSCompiler) isIteratorSignature(sig *types.Signature) bool {
116
+ params := sig.Params()
117
+ if params.Len() != 1 {
118
+ return false
119
+ }
120
+ yieldParam := params.At(0).Type()
121
+ if yieldSig, ok := yieldParam.Underlying().(*types.Signature); ok {
122
+ yieldResults := yieldSig.Results()
123
+ if yieldResults.Len() == 1 {
124
+ if basic, ok := yieldResults.At(0).Type().Underlying().(*types.Basic); ok && basic.Kind() == types.Bool {
125
+ return true
525
126
  }
526
- c.tsw.WriteLiterally(") => {")
527
- } else {
528
- // No variables iterator
529
- c.tsw.WriteLiterally("(() => {")
530
127
  }
128
+ }
129
+ return false
130
+ }
131
+
132
+ func (c *GoToTSCompiler) getIndexVarName(exp *ast.RangeStmt, defaultName string) string {
133
+ if exp.Key != nil {
134
+ if keyIdent, ok := exp.Key.(*ast.Ident); ok && keyIdent.Name != "_" {
135
+ return keyIdent.Name
136
+ }
137
+ }
138
+ return defaultName
139
+ }
531
140
 
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)
141
+ func (c *GoToTSCompiler) writeMapRange(exp *ast.RangeStmt) error {
142
+ keyVarName := "_k"
143
+ valueVarName := "_v"
144
+
145
+ if exp.Key != nil {
146
+ if ident, ok := exp.Key.(*ast.Ident); ok && ident.Name != "_" {
147
+ keyVarName = ident.Name
536
148
  }
537
- c.tsw.WriteLiterally("return shouldContinue")
538
- c.tsw.WriteLine("")
539
- c.tsw.Indent(-1)
540
- c.tsw.WriteLiterally("})")
149
+ }
150
+ if exp.Value != nil {
151
+ if ident, ok := exp.Value.(*ast.Ident); ok && ident.Name != "_" {
152
+ valueVarName = ident.Name
153
+ }
154
+ }
155
+
156
+ c.tsw.WriteLiterallyf("for (const [%s, %s] of ", keyVarName, valueVarName)
157
+ if err := c.WriteValueExpr(exp.X); err != nil {
158
+ return fmt.Errorf("failed to write range loop map expression: %w", err)
159
+ }
160
+ c.tsw.WriteLiterally(".entries()) {")
161
+ c.tsw.Indent(1)
162
+ c.tsw.WriteLine("")
163
+
164
+ if err := c.WriteStmtBlock(exp.Body, false); err != nil {
165
+ return fmt.Errorf("failed to write range loop map body: %w", err)
166
+ }
167
+ c.tsw.Indent(-1)
168
+ c.tsw.WriteLine("}")
169
+ return nil
170
+ }
171
+
172
+ func (c *GoToTSCompiler) writeStringRange(exp *ast.RangeStmt) error {
173
+ c.tsw.WriteLine("{")
174
+ c.tsw.Indent(1)
175
+
176
+ c.tsw.WriteLiterally("const _runes = $.stringToRunes(")
177
+ if err := c.WriteValueExpr(exp.X); err != nil {
178
+ return fmt.Errorf("failed to write range loop string conversion expression: %w", err)
179
+ }
180
+ c.tsw.WriteLiterally(")")
181
+ c.tsw.WriteLine("")
182
+
183
+ indexVarName := c.getIndexVarName(exp, "i")
184
+ c.tsw.WriteLiterallyf("for (let %s = 0; %s < _runes.length; %s++) {", indexVarName, indexVarName, indexVarName)
185
+ c.tsw.Indent(1)
186
+ c.tsw.WriteLine("")
187
+
188
+ if exp.Value != nil {
189
+ if ident, ok := exp.Value.(*ast.Ident); ok && ident.Name != "_" {
190
+ c.tsw.WriteLiterally("const ")
191
+ c.WriteIdent(ident, false)
192
+ c.tsw.WriteLiterally(" = _runes[i]") // TODO: should be indexVarName?
193
+ c.tsw.WriteLine("")
194
+ }
195
+ }
196
+
197
+ if err := c.WriteStmtBlock(exp.Body, false); err != nil {
198
+ return fmt.Errorf("failed to write range loop string body: %w", err)
199
+ }
200
+ c.tsw.Indent(-1)
201
+ c.tsw.WriteLine("}")
202
+ c.tsw.Indent(-1)
203
+ c.tsw.WriteLine("}")
204
+ return nil
205
+ }
206
+
207
+ func (c *GoToTSCompiler) writeIntegerRange(exp *ast.RangeStmt) error {
208
+ if exp.Value != nil {
209
+ return errors.Errorf("ranging over an integer supports key variable only (not value variable): %v", exp)
210
+ }
211
+
212
+ indexVarName := c.getIndexVarName(exp, "_i")
213
+ c.tsw.WriteLiterallyf("for (let %s = 0; %s < ", indexVarName, indexVarName)
214
+ if err := c.WriteValueExpr(exp.X); err != nil {
215
+ return fmt.Errorf("failed to write range loop integer expression: %w", err)
216
+ }
217
+ c.tsw.WriteLiterallyf("; %s++) {", indexVarName)
218
+
219
+ if err := c.WriteStmtBlock(exp.Body, false); err != nil {
220
+ return fmt.Errorf("failed to write range loop integer body: %w", err)
221
+ }
222
+
223
+ c.tsw.Indent(-1)
224
+ c.tsw.WriteLine("}")
225
+ return nil
226
+ }
227
+
228
+ func (c *GoToTSCompiler) writeArraySliceRange(exp *ast.RangeStmt, isPointer bool) error {
229
+ indexVarName := c.getIndexVarName(exp, "_i")
230
+
231
+ // Handle the different cases
232
+ if exp.Key != nil && exp.Value != nil {
233
+ return c.writeArraySliceWithKeyValue(exp, indexVarName, isPointer)
234
+ } else if exp.Key != nil && exp.Value == nil {
235
+ return c.writeArraySliceKeyOnly(exp, indexVarName, isPointer)
236
+ } else if exp.Key == nil && exp.Value != nil {
237
+ return errors.Errorf("unexpected value without key in for range expression: %v", exp)
238
+ } else {
239
+ return c.writeArraySliceFallback(exp, isPointer)
240
+ }
241
+ }
242
+
243
+ func (c *GoToTSCompiler) writeArraySliceWithKeyValue(exp *ast.RangeStmt, indexVarName string, isPointer bool) error {
244
+ c.tsw.WriteLiterallyf("for (let %s = 0; %s < $.len(", indexVarName, indexVarName)
245
+ if err := c.writeArraySliceExpression(exp.X, isPointer); err != nil {
246
+ return fmt.Errorf("failed to write range loop array/slice expression (key and value): %w", err)
247
+ }
248
+ c.tsw.WriteLiterallyf("); %s++) {", indexVarName)
249
+ c.tsw.Indent(1)
250
+ c.tsw.WriteLine("")
251
+
252
+ if ident, ok := exp.Value.(*ast.Ident); ok && ident.Name != "_" {
253
+ c.tsw.WriteLiterally("const ")
254
+ c.WriteIdent(ident, false)
255
+ c.tsw.WriteLiterally(" = ")
256
+ if err := c.writeArraySliceExpression(exp.X, isPointer); err != nil {
257
+ return fmt.Errorf("failed to write range loop array/slice value expression: %w", err)
258
+ }
259
+ c.tsw.WriteLiterallyf("![%s]", indexVarName)
541
260
  c.tsw.WriteLine("")
542
- c.tsw.Indent(-1)
543
- c.tsw.WriteLine("})()")
261
+ }
262
+
263
+ if err := c.WriteStmt(exp.Body); err != nil {
264
+ return fmt.Errorf("failed to write range loop array/slice body (key and value): %w", err)
265
+ }
266
+ c.tsw.Indent(-1)
267
+ c.tsw.WriteLine("}")
268
+ return nil
269
+ }
270
+
271
+ func (c *GoToTSCompiler) writeArraySliceKeyOnly(exp *ast.RangeStmt, indexVarName string, isPointer bool) error {
272
+ c.tsw.WriteLiterallyf("for (let %s = 0; %s < $.len(", indexVarName, indexVarName)
273
+ if err := c.writeArraySliceExpression(exp.X, isPointer); err != nil {
274
+ return fmt.Errorf("failed to write expression for the iterable: %w", err)
275
+ }
276
+ c.tsw.WriteLiterallyf("); %s++) {", indexVarName)
277
+ c.tsw.Indent(1)
278
+ c.tsw.WriteLine("")
279
+
280
+ if err := c.WriteStmtBlock(exp.Body, false); err != nil {
281
+ return fmt.Errorf("failed to write range loop array/slice body (only key): %w", err)
282
+ }
283
+ c.tsw.Indent(-1)
284
+ c.tsw.WriteLine("}")
285
+ return nil
286
+ }
287
+
288
+ func (c *GoToTSCompiler) writeArraySliceFallback(exp *ast.RangeStmt, isPointer bool) error {
289
+ indexVarName := "_i"
290
+ c.tsw.WriteLiterallyf("for (let %s = 0; %s < $.len(", indexVarName, indexVarName)
291
+ if err := c.writeArraySliceExpression(exp.X, isPointer); err != nil {
292
+ return fmt.Errorf("failed to write range loop array/slice length expression (fallback): %w", err)
293
+ }
294
+ c.tsw.WriteLiterallyf("); %s++) {", indexVarName)
295
+ c.tsw.Indent(1)
296
+ c.tsw.WriteLine("")
297
+
298
+ if err := c.WriteStmtBlock(exp.Body, false); err != nil {
299
+ return fmt.Errorf("failed to write range loop array/slice body (fallback): %w", err)
300
+ }
301
+ c.tsw.Indent(-1)
302
+ c.tsw.WriteLine("}")
303
+ return nil
304
+ }
305
+
306
+ func (c *GoToTSCompiler) writeArraySliceExpression(expr ast.Expr, isPointer bool) error {
307
+ if isPointer {
308
+ if ident, ok := expr.(*ast.Ident); ok {
309
+ c.WriteIdent(ident, false)
310
+ } else {
311
+ if err := c.WriteValueExpr(expr); err != nil {
312
+ return err
313
+ }
314
+ }
315
+ c.tsw.WriteLiterally("!.value")
544
316
  return nil
317
+ } else {
318
+ return c.WriteValueExpr(expr)
545
319
  }
320
+ }
546
321
 
547
- return errors.Errorf("unsupported range loop type: %T for expression %v", underlying, exp)
322
+ func (c *GoToTSCompiler) writeIteratorRange(exp *ast.RangeStmt, sig *types.Signature) error {
323
+ params := sig.Params()
324
+ yieldParam := params.At(0).Type()
325
+ yieldSig := yieldParam.Underlying().(*types.Signature)
326
+ yieldParams := yieldSig.Params()
327
+
328
+ c.tsw.WriteLiterally(";(() => {")
329
+ c.tsw.Indent(1)
330
+ c.tsw.WriteLine("")
331
+ c.tsw.WriteLiterally("let shouldContinue = true")
332
+ c.tsw.WriteLine("")
333
+
334
+ if err := c.WriteValueExpr(exp.X); err != nil {
335
+ return fmt.Errorf("failed to write iterator expression: %w", err)
336
+ }
337
+
338
+ switch yieldParams.Len() {
339
+ case 0:
340
+ c.tsw.WriteLiterally("!(() => {")
341
+ case 1:
342
+ c.tsw.WriteLiterally("!((")
343
+ c.writeIteratorParam(exp.Value, "v")
344
+ c.tsw.WriteLiterally(") => {")
345
+ case 2:
346
+ c.tsw.WriteLiterally("!((")
347
+ c.writeIteratorParam(exp.Key, "k")
348
+ c.tsw.WriteLiterally(", ")
349
+ c.writeIteratorParam(exp.Value, "v")
350
+ c.tsw.WriteLiterally(") => {")
351
+ }
352
+
353
+ c.tsw.Indent(1)
354
+ c.tsw.WriteLine("")
355
+ if err := c.WriteStmtBlock(exp.Body, false); err != nil {
356
+ return fmt.Errorf("failed to write iterator body: %w", err)
357
+ }
358
+ c.tsw.WriteLiterally("return shouldContinue")
359
+ c.tsw.WriteLine("")
360
+ c.tsw.Indent(-1)
361
+ c.tsw.WriteLiterally("})")
362
+ c.tsw.WriteLine("")
363
+ c.tsw.Indent(-1)
364
+ c.tsw.WriteLine("})()")
365
+ return nil
366
+ }
367
+
368
+ func (c *GoToTSCompiler) writeIteratorParam(param ast.Expr, defaultName string) {
369
+ if param != nil {
370
+ if ident, ok := param.(*ast.Ident); ok && ident.Name != "_" {
371
+ c.WriteIdent(ident, false)
372
+ return
373
+ }
374
+ }
375
+ c.tsw.WriteLiterally(defaultName)
376
+ }
377
+
378
+ func (c *GoToTSCompiler) writeInterfaceIteratorRange(exp *ast.RangeStmt) error {
379
+ c.tsw.WriteLiterally(";(() => {")
380
+ c.tsw.Indent(1)
381
+ c.tsw.WriteLine("")
382
+ c.tsw.WriteLiterally("let shouldContinue = true")
383
+ c.tsw.WriteLine("")
384
+
385
+ if err := c.WriteValueExpr(exp.X); err != nil {
386
+ return fmt.Errorf("failed to write interface iterator expression: %w", err)
387
+ }
388
+
389
+ if exp.Key != nil && exp.Value != nil {
390
+ c.tsw.WriteLiterally("!((")
391
+ c.writeIteratorParam(exp.Key, "k")
392
+ c.tsw.WriteLiterally(", ")
393
+ c.writeIteratorParam(exp.Value, "v")
394
+ c.tsw.WriteLiterally(") => {")
395
+ } else if exp.Value != nil {
396
+ c.tsw.WriteLiterally("!((")
397
+ c.writeIteratorParam(exp.Value, "v")
398
+ c.tsw.WriteLiterally(") => {")
399
+ } else if exp.Key != nil {
400
+ c.tsw.WriteLiterally("!((")
401
+ c.writeIteratorParam(exp.Key, "k")
402
+ c.tsw.WriteLiterally(") => {")
403
+ } else {
404
+ c.tsw.WriteLiterally("!(() => {")
405
+ }
406
+
407
+ c.tsw.Indent(1)
408
+ c.tsw.WriteLine("")
409
+ if err := c.WriteStmtBlock(exp.Body, false); err != nil {
410
+ return fmt.Errorf("failed to write interface iterator body: %w", err)
411
+ }
412
+ c.tsw.WriteLiterally("return shouldContinue")
413
+ c.tsw.WriteLine("")
414
+ c.tsw.Indent(-1)
415
+ c.tsw.WriteLiterally("})")
416
+ c.tsw.WriteLine("")
417
+ c.tsw.Indent(-1)
418
+ c.tsw.WriteLine("})()")
419
+ return nil
548
420
  }