goscript 0.0.22 → 0.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +1 -1
  2. package/cmd/goscript/cmd_compile.go +3 -3
  3. package/compiler/analysis.go +302 -182
  4. package/compiler/analysis_test.go +220 -0
  5. package/compiler/assignment.go +42 -43
  6. package/compiler/builtin_test.go +102 -0
  7. package/compiler/compiler.go +117 -29
  8. package/compiler/compiler_test.go +36 -8
  9. package/compiler/composite-lit.go +133 -53
  10. package/compiler/config.go +7 -3
  11. package/compiler/config_test.go +6 -33
  12. package/compiler/decl.go +36 -0
  13. package/compiler/expr-call.go +116 -60
  14. package/compiler/expr-selector.go +88 -43
  15. package/compiler/expr-star.go +57 -65
  16. package/compiler/expr-type.go +132 -5
  17. package/compiler/expr-value.go +8 -38
  18. package/compiler/expr.go +326 -30
  19. package/compiler/field.go +3 -3
  20. package/compiler/lit.go +34 -2
  21. package/compiler/primitive.go +19 -12
  22. package/compiler/spec-struct.go +140 -9
  23. package/compiler/spec-value.go +119 -41
  24. package/compiler/spec.go +21 -6
  25. package/compiler/stmt-assign.go +65 -3
  26. package/compiler/stmt-for.go +11 -0
  27. package/compiler/stmt-range.go +119 -11
  28. package/compiler/stmt-select.go +211 -0
  29. package/compiler/stmt-type-switch.go +147 -0
  30. package/compiler/stmt.go +175 -238
  31. package/compiler/type-assert.go +125 -379
  32. package/compiler/type.go +216 -129
  33. package/dist/gs/builtin/builtin.js +37 -0
  34. package/dist/gs/builtin/builtin.js.map +1 -0
  35. package/dist/gs/builtin/channel.js +471 -0
  36. package/dist/gs/builtin/channel.js.map +1 -0
  37. package/dist/gs/builtin/defer.js +54 -0
  38. package/dist/gs/builtin/defer.js.map +1 -0
  39. package/dist/gs/builtin/io.js +15 -0
  40. package/dist/gs/builtin/io.js.map +1 -0
  41. package/dist/gs/builtin/map.js +44 -0
  42. package/dist/gs/builtin/map.js.map +1 -0
  43. package/dist/gs/builtin/slice.js +799 -0
  44. package/dist/gs/builtin/slice.js.map +1 -0
  45. package/dist/gs/builtin/type.js +745 -0
  46. package/dist/gs/builtin/type.js.map +1 -0
  47. package/dist/gs/builtin/varRef.js +14 -0
  48. package/dist/gs/builtin/varRef.js.map +1 -0
  49. package/dist/gs/context/context.js +55 -0
  50. package/dist/gs/context/context.js.map +1 -0
  51. package/dist/gs/context/index.js +2 -0
  52. package/dist/gs/context/index.js.map +1 -0
  53. package/dist/gs/runtime/index.js +2 -0
  54. package/dist/gs/runtime/index.js.map +1 -0
  55. package/dist/gs/runtime/runtime.js +158 -0
  56. package/dist/gs/runtime/runtime.js.map +1 -0
  57. package/dist/gs/time/index.js +2 -0
  58. package/dist/gs/time/index.js.map +1 -0
  59. package/dist/gs/time/time.js +115 -0
  60. package/dist/gs/time/time.js.map +1 -0
  61. package/package.json +7 -6
  62. package/builtin/builtin.go +0 -11
  63. package/builtin/builtin.ts +0 -2379
  64. package/dist/builtin/builtin.d.ts +0 -513
  65. package/dist/builtin/builtin.js +0 -1686
  66. package/dist/builtin/builtin.js.map +0 -1
package/compiler/stmt.go CHANGED
@@ -28,12 +28,13 @@ import (
28
28
  // - Go statements (`ast.GoStmt`): `WriteStmtGo`.
29
29
  // - Select statements (`ast.SelectStmt`): `WriteStmtSelect`.
30
30
  // - Branch statements (`ast.BranchStmt`): `WriteStmtBranch`.
31
+ // - Type switch statements (`ast.TypeSwitchStmt`): `WriteStmtTypeSwitch`.
32
+ // - Labeled statements (`ast.LabeledStmt`): `WriteStmtLabeled`.
31
33
  //
32
34
  // If an unknown statement type is encountered, it returns an error.
33
35
  func (c *GoToTSCompiler) WriteStmt(a ast.Stmt) error {
34
36
  switch exp := a.(type) {
35
37
  case *ast.BlockStmt:
36
- // WriteStmtBlock does not currently return an error, assuming it's safe for now.
37
38
  if err := c.WriteStmtBlock(exp, false); err != nil {
38
39
  return fmt.Errorf("failed to write block statement: %w", err)
39
40
  }
@@ -73,7 +74,6 @@ func (c *GoToTSCompiler) WriteStmt(a ast.Stmt) error {
73
74
  return fmt.Errorf("failed to write declaration statement: %w", err)
74
75
  }
75
76
  case *ast.ForStmt:
76
- // WriteStmtFor does not currently return an error, assuming it's safe for now.
77
77
  if err := c.WriteStmtFor(exp); err != nil {
78
78
  return fmt.Errorf("failed to write for statement: %w", err)
79
79
  }
@@ -83,7 +83,6 @@ func (c *GoToTSCompiler) WriteStmt(a ast.Stmt) error {
83
83
  return fmt.Errorf("failed to write range statement: %w", err)
84
84
  }
85
85
  case *ast.SwitchStmt:
86
- // WriteStmtSwitch does not currently return an error, assuming it's safe for now.
87
86
  if err := c.WriteStmtSwitch(exp); err != nil {
88
87
  return fmt.Errorf("failed to write switch statement: %w", err)
89
88
  }
@@ -108,8 +107,16 @@ func (c *GoToTSCompiler) WriteStmt(a ast.Stmt) error {
108
107
  if err := c.WriteStmtBranch(exp); err != nil {
109
108
  return fmt.Errorf("failed to write branch statement: %w", err)
110
109
  }
110
+ case *ast.TypeSwitchStmt:
111
+ if err := c.WriteStmtTypeSwitch(exp); err != nil {
112
+ return fmt.Errorf("failed to write type switch statement: %w", err)
113
+ }
114
+ case *ast.LabeledStmt:
115
+ if err := c.WriteStmtLabeled(exp); err != nil {
116
+ return fmt.Errorf("failed to write labeled statement: %w", err)
117
+ }
111
118
  default:
112
- return errors.Errorf("unknown statement: %s\n", a)
119
+ return errors.Errorf("unknown statement: %#v\n", a)
113
120
  }
114
121
  return nil
115
122
  }
@@ -172,17 +179,17 @@ func (c *GoToTSCompiler) WriteStmtBranch(stmt *ast.BranchStmt) error {
172
179
  }
173
180
 
174
181
  // WriteStmtGo translates a Go statement (`ast.GoStmt`) into its TypeScript equivalent.
175
- // It handles `go func(){...}()` and `go namedFunc(args)`.
182
+ // It handles `go func(){...}()`, `go namedFunc(args)`, and `go x.Method(args)`.
176
183
  func (c *GoToTSCompiler) WriteStmtGo(exp *ast.GoStmt) error {
177
184
  // Handle goroutine statement
178
185
  // Translate 'go func() { ... }()' to 'queueMicrotask(() => { ... compiled body ... })'
179
-
180
- // The call expression's function is the function literal
181
186
  callExpr := exp.Call
182
- if funcLit, ok := callExpr.Fun.(*ast.FuncLit); ok {
187
+
188
+ switch fun := callExpr.Fun.(type) {
189
+ case *ast.FuncLit:
183
190
  // For function literals, we need to check if the function literal itself is async
184
191
  // This happens during analysis in analysisVisitor.Visit for FuncLit nodes
185
- isAsync := c.analysis.IsFuncLitAsync(funcLit)
192
+ isAsync := c.analysis.IsFuncLitAsync(fun)
186
193
  if isAsync {
187
194
  c.tsw.WriteLiterally("queueMicrotask(async () => ")
188
195
  } else {
@@ -190,18 +197,18 @@ func (c *GoToTSCompiler) WriteStmtGo(exp *ast.GoStmt) error {
190
197
  }
191
198
 
192
199
  // Compile the function literal's body directly
193
- if err := c.WriteStmtBlock(funcLit.Body, true); err != nil {
200
+ if err := c.WriteStmtBlock(fun.Body, true); err != nil {
194
201
  return fmt.Errorf("failed to write goroutine function literal body: %w", err)
195
202
  }
196
203
 
197
204
  c.tsw.WriteLine(")") // Close the queueMicrotask statement
198
205
 
199
- } else if ident, ok := callExpr.Fun.(*ast.Ident); ok {
206
+ case *ast.Ident:
200
207
  // Handle named functions: go namedFunc(args)
201
208
  // Get the object for this function
202
- obj := c.pkg.TypesInfo.Uses[ident]
209
+ obj := c.pkg.TypesInfo.Uses[fun]
203
210
  if obj == nil {
204
- return errors.Errorf("could not find object for function: %s", ident.Name)
211
+ return errors.Errorf("could not find object for function: %s", fun.Name)
205
212
  }
206
213
 
207
214
  // Check if the function is async
@@ -221,7 +228,7 @@ func (c *GoToTSCompiler) WriteStmtGo(exp *ast.GoStmt) error {
221
228
  }
222
229
 
223
230
  // Write the function name
224
- c.tsw.WriteLiterally(ident.Name)
231
+ c.tsw.WriteLiterally(fun.Name)
225
232
 
226
233
  // Write the function arguments
227
234
  c.tsw.WriteLiterally("(")
@@ -238,7 +245,93 @@ func (c *GoToTSCompiler) WriteStmtGo(exp *ast.GoStmt) error {
238
245
 
239
246
  c.tsw.Indent(-1)
240
247
  c.tsw.WriteLine("})") // Close the queueMicrotask callback and the statement
241
- } else {
248
+ case *ast.SelectorExpr:
249
+ // Handle selector expressions: go x.Method(args)
250
+ // Get the object for the selected method
251
+ obj := c.pkg.TypesInfo.Uses[fun.Sel]
252
+ if obj == nil {
253
+ return errors.Errorf("could not find object for selected method: %s", fun.Sel.Name)
254
+ }
255
+
256
+ // Check if the function is async
257
+ isAsync := c.analysis.IsAsyncFunc(obj)
258
+ if isAsync {
259
+ c.tsw.WriteLiterally("queueMicrotask(async () => {")
260
+ } else {
261
+ c.tsw.WriteLiterally("queueMicrotask(() => {")
262
+ }
263
+
264
+ c.tsw.Indent(1)
265
+ c.tsw.WriteLine("")
266
+
267
+ // Write the function call, using await if the function is async
268
+ if isAsync {
269
+ c.tsw.WriteLiterally("await ")
270
+ }
271
+
272
+ // Write the selector expression (e.g., f.Bar)
273
+ // Note: callExpr.Fun is the *ast.SelectorExpr itself
274
+ // For method calls, we need to add null assertion since Go would panic on nil receiver
275
+ if selectorExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
276
+ if err := c.WriteValueExpr(selectorExpr.X); err != nil {
277
+ return fmt.Errorf("failed to write selector base expression in goroutine: %w", err)
278
+ }
279
+ // Add null assertion for method calls - Go would panic if receiver is nil
280
+ c.tsw.WriteLiterally("!.")
281
+ c.WriteIdent(selectorExpr.Sel, true)
282
+ } else {
283
+ if err := c.WriteValueExpr(callExpr.Fun); err != nil {
284
+ return fmt.Errorf("failed to write selector expression in goroutine: %w", err)
285
+ }
286
+ }
287
+
288
+ // Write the function arguments
289
+ c.tsw.WriteLiterally("(")
290
+ for i, arg := range callExpr.Args {
291
+ if i != 0 {
292
+ c.tsw.WriteLiterally(", ")
293
+ }
294
+ if err := c.WriteValueExpr(arg); err != nil {
295
+ return fmt.Errorf("failed to write argument %d in goroutine selector function call: %w", i, err)
296
+ }
297
+ }
298
+ c.tsw.WriteLiterally(")")
299
+ c.tsw.WriteLine("")
300
+
301
+ c.tsw.Indent(-1)
302
+ c.tsw.WriteLine("})") // Close the queueMicrotask callback and the statement
303
+ case *ast.TypeAssertExpr:
304
+ // Handle type assertion expressions: go x.(func())()
305
+ // We assume this is always synchronous (no async function returned by type assertion)
306
+ c.tsw.WriteLiterally("queueMicrotask(() => {")
307
+
308
+ c.tsw.Indent(1)
309
+ c.tsw.WriteLine("")
310
+
311
+ // Write the type assertion call
312
+ if err := c.WriteTypeAssertExpr(fun); err != nil {
313
+ return fmt.Errorf("failed to write type assertion expression in goroutine: %w", err)
314
+ }
315
+
316
+ // Add non-null assertion since mustTypeAssert throws on failure rather than returning null
317
+ c.tsw.WriteLiterally("!")
318
+
319
+ // Write the function arguments
320
+ c.tsw.WriteLiterally("(")
321
+ for i, arg := range callExpr.Args {
322
+ if i != 0 {
323
+ c.tsw.WriteLiterally(", ")
324
+ }
325
+ if err := c.WriteValueExpr(arg); err != nil {
326
+ return fmt.Errorf("failed to write argument %d in goroutine type assertion function call: %w", i, err)
327
+ }
328
+ }
329
+ c.tsw.WriteLiterally(")")
330
+ c.tsw.WriteLine("")
331
+
332
+ c.tsw.Indent(-1)
333
+ c.tsw.WriteLine("})") // Close the queueMicrotask callback and the statement
334
+ default:
242
335
  return errors.Errorf("unhandled goroutine function type: %T", callExpr.Fun)
243
336
  }
244
337
  return nil
@@ -258,12 +351,12 @@ func (c *GoToTSCompiler) WriteStmtGo(exp *ast.GoStmt) error {
258
351
  func (c *GoToTSCompiler) WriteStmtExpr(exp *ast.ExprStmt) error {
259
352
  // Handle simple channel receive used as a statement (<-ch)
260
353
  if unaryExpr, ok := exp.X.(*ast.UnaryExpr); ok && unaryExpr.Op == token.ARROW {
261
- // Translate <-ch to await ch.receive()
262
- c.tsw.WriteLiterally("await ")
354
+ // Translate <-ch to await $.chanRecv(ch)
355
+ c.tsw.WriteLiterally("await $.chanRecv(")
263
356
  if err := c.WriteValueExpr(unaryExpr.X); err != nil { // Channel expression
264
357
  return fmt.Errorf("failed to write channel expression in receive statement: %w", err)
265
358
  }
266
- c.tsw.WriteLiterally(".receive()") // Use receive() as the value is discarded
359
+ c.tsw.WriteLiterally(")") // Use chanRecv() as the value is discarded
267
360
  c.tsw.WriteLine("")
268
361
  return nil
269
362
  }
@@ -315,12 +408,12 @@ func (c *GoToTSCompiler) WriteStmtExpr(exp *ast.ExprStmt) error {
315
408
  // channel send operations are asynchronous in the TypeScript model.
316
409
  // The statement is terminated with a newline.
317
410
  func (c *GoToTSCompiler) WriteStmtSend(exp *ast.SendStmt) error {
318
- // Translate ch <- value to await ch.send(value)
319
- c.tsw.WriteLiterally("await ")
411
+ // Translate ch <- value to await $.chanSend(ch, value)
412
+ c.tsw.WriteLiterally("await $.chanSend(")
320
413
  if err := c.WriteValueExpr(exp.Chan); err != nil { // The channel expression
321
414
  return fmt.Errorf("failed to write channel expression in send statement: %w", err)
322
415
  }
323
- c.tsw.WriteLiterally(".send(")
416
+ c.tsw.WriteLiterally(", ")
324
417
  if err := c.WriteValueExpr(exp.Value); err != nil { // The value expression
325
418
  return fmt.Errorf("failed to write value expression in send statement: %w", err)
326
419
  }
@@ -346,16 +439,19 @@ func (c *GoToTSCompiler) WriteStmtSend(exp *ast.SendStmt) error {
346
439
  // The function aims to produce idiomatic TypeScript `if/else if/else` structures.
347
440
  func (s *GoToTSCompiler) WriteStmtIf(exp *ast.IfStmt) error {
348
441
  if exp.Init != nil {
349
- s.tsw.WriteLiterally("{")
350
- s.tsw.Indent(1)
442
+ s.tsw.WriteLiterally("{") // Write opening brace
443
+ s.tsw.WriteLine("") // Add newline immediately after opening brace
444
+ s.tsw.Indent(1) // Indent for the initializer
351
445
 
352
- if err := s.WriteStmt(exp.Init); err != nil {
446
+ if err := s.WriteStmt(exp.Init); err != nil { // Write the initializer
353
447
  return err
354
448
  }
355
449
 
450
+ // This defer handles closing the synthetic block for the initializer
356
451
  defer func() {
357
452
  s.tsw.Indent(-1)
358
- s.tsw.WriteLiterally("}")
453
+ s.tsw.WriteLiterally("}") // Write the closing brace at the now-correct indent level
454
+ s.tsw.WriteLine("") // Ensure a newline *after* this '}', critical for preventing '}}'
359
455
  }()
360
456
  }
361
457
 
@@ -406,21 +502,51 @@ func (s *GoToTSCompiler) WriteStmtIf(exp *ast.IfStmt) error {
406
502
  // The statement is terminated with a newline.
407
503
  func (c *GoToTSCompiler) WriteStmtReturn(exp *ast.ReturnStmt) error {
408
504
  c.tsw.WriteLiterally("return ")
409
- if len(exp.Results) > 1 {
410
- c.tsw.WriteLiterally("[")
411
- }
412
- for i, res := range exp.Results {
413
- if i != 0 {
414
- c.tsw.WriteLiterally(", ")
505
+
506
+ // Check if it's a bare named return
507
+ nodeInfo := c.analysis.NodeData[exp]
508
+ if nodeInfo != nil && nodeInfo.IsBareReturn {
509
+ var namedReturns []string
510
+ if nodeInfo.EnclosingFuncDecl != nil {
511
+ if obj := c.pkg.TypesInfo.ObjectOf(nodeInfo.EnclosingFuncDecl.Name); obj != nil {
512
+ if funcInfo := c.analysis.FunctionData[obj]; funcInfo != nil {
513
+ namedReturns = funcInfo.NamedReturns
514
+ }
515
+ }
516
+ } else if nodeInfo.EnclosingFuncLit != nil {
517
+ if funcInfo := c.analysis.FuncLitData[nodeInfo.EnclosingFuncLit]; funcInfo != nil {
518
+ namedReturns = funcInfo.NamedReturns
519
+ }
415
520
  }
416
- if err := c.WriteValueExpr(res); err != nil { // Return results are values
417
- return err
521
+
522
+ if len(namedReturns) > 0 {
523
+ c.tsw.WriteLiterally("[")
524
+ for i, name := range namedReturns {
525
+ if i != 0 {
526
+ c.tsw.WriteLiterally(", ")
527
+ }
528
+ c.tsw.WriteLiterally(name)
529
+ }
530
+ c.tsw.WriteLiterally("]")
531
+ }
532
+ } else {
533
+ // Handle explicit return values
534
+ if len(exp.Results) > 1 {
535
+ c.tsw.WriteLiterally("[")
536
+ }
537
+ for i, res := range exp.Results {
538
+ if i != 0 {
539
+ c.tsw.WriteLiterally(", ")
540
+ }
541
+ if err := c.WriteValueExpr(res); err != nil { // Return results are values
542
+ return err
543
+ }
544
+ }
545
+ if len(exp.Results) > 1 {
546
+ c.tsw.WriteLiterally("]")
418
547
  }
419
548
  }
420
- if len(exp.Results) > 1 {
421
- c.tsw.WriteLiterally("]")
422
- }
423
- c.tsw.WriteLine("") // Remove semicolon
549
+ c.tsw.WriteLine("")
424
550
  return nil
425
551
  }
426
552
 
@@ -464,7 +590,7 @@ func (c *GoToTSCompiler) WriteStmtBlock(exp *ast.BlockStmt, suppressNewline bool
464
590
  for _, stmt := range exp.List {
465
591
  if deferStmt, ok := stmt.(*ast.DeferStmt); ok {
466
592
  if funcLit, ok := deferStmt.Call.Fun.(*ast.FuncLit); ok {
467
- if c.analysis.IsInAsyncFunctionMap[funcLit] {
593
+ if c.analysis.IsFuncLitAsync(funcLit) {
468
594
  hasAsyncDefer = true
469
595
  break
470
596
  }
@@ -530,7 +656,6 @@ func (c *GoToTSCompiler) WriteStmtBlock(exp *ast.BlockStmt, suppressNewline bool
530
656
  start = file.Line(cg.Pos())
531
657
  }
532
658
  writeBlank(lastLine, start)
533
- // WriteDoc does not currently return an error, assuming it's safe for now.
534
659
  c.WriteDoc(cg) // WriteDoc will handle the actual comment text
535
660
  if file != nil && cg.End().IsValid() {
536
661
  lastLine = file.Line(cg.End())
@@ -567,7 +692,6 @@ func (c *GoToTSCompiler) WriteStmtBlock(exp *ast.BlockStmt, suppressNewline bool
567
692
  // only emit if it follows the last content
568
693
  if start > lastLine {
569
694
  writeBlank(lastLine, start)
570
- // WriteDoc does not currently return an error, assuming it's safe for now.
571
695
  c.WriteDoc(cg)
572
696
  if file != nil && cg.End().IsValid() {
573
697
  lastLine = file.Line(cg.End())
@@ -634,7 +758,6 @@ func (c *GoToTSCompiler) WriteStmtSwitch(exp *ast.SwitchStmt) error {
634
758
  // Handle case clauses
635
759
  for _, stmt := range exp.Body.List {
636
760
  if caseClause, ok := stmt.(*ast.CaseClause); ok {
637
- // WriteCaseClause does not currently return an error, assuming it's safe for now.
638
761
  if err := c.WriteCaseClause(caseClause); err != nil {
639
762
  return fmt.Errorf("failed to write case clause in switch statement: %w", err)
640
763
  }
@@ -667,7 +790,7 @@ func (c *GoToTSCompiler) WriteStmtDefer(exp *ast.DeferStmt) error {
667
790
  // Determine if the deferred call is to an async function literal using analysis
668
791
  isAsyncDeferred := false
669
792
  if funcLit, ok := exp.Call.Fun.(*ast.FuncLit); ok {
670
- isAsyncDeferred = c.analysis.IsInAsyncFunctionMap[funcLit]
793
+ isAsyncDeferred = c.analysis.IsFuncLitAsync(funcLit)
671
794
  }
672
795
 
673
796
  // Set async prefix based on pre-computed async status
@@ -696,6 +819,7 @@ func (c *GoToTSCompiler) WriteStmtDefer(exp *ast.DeferStmt) error {
696
819
  if err := c.WriteValueExpr(exp.Call); err != nil {
697
820
  return fmt.Errorf("failed to write deferred call: %w", err)
698
821
  }
822
+ c.tsw.WriteLine("")
699
823
  }
700
824
 
701
825
  c.tsw.Indent(-1)
@@ -704,204 +828,17 @@ func (c *GoToTSCompiler) WriteStmtDefer(exp *ast.DeferStmt) error {
704
828
  return nil
705
829
  }
706
830
 
707
- // WriteStmtSelect translates a Go `select` statement into an asynchronous
708
- // TypeScript operation using the `$.selectStatement` runtime helper.
709
- // Go's `select` provides non-deterministic choice over channel operations.
710
- // This is emulated by constructing an array of `SelectCase` objects, one for
711
- // each `case` in the Go `select`, and passing it to `$.selectStatement`.
712
- //
713
- // Each `SelectCase` object includes:
714
- // - `id`: A unique identifier for the case.
715
- // - `isSend`: `true` for send operations (`case ch <- val:`), `false` for receives.
716
- // - `channel`: The TypeScript channel object.
717
- // - `value` (for sends): The value being sent.
718
- // - `onSelected: async (result) => { ... }`: A callback executed when this case
719
- // is chosen. `result` contains `{ value, ok }` for receives.
720
- // - Inside `onSelected`, assignments for receive operations (e.g., `v := <-ch`,
721
- // `v, ok := <-ch`) are handled by declaring/assigning variables from `result.value`
722
- // and `result.ok`.
723
- // - The original Go case body is then translated within this callback.
724
- //
725
- // A `default` case in Go `select` is translated to a `SelectCase` with `id: -1`
726
- // and its body in the `onSelected` handler. The `$.selectStatement` helper
727
- // is informed if a default case exists.
728
- // The entire `$.selectStatement(...)` call is `await`ed because channel
729
- // operations are asynchronous in the TypeScript model.
730
- func (c *GoToTSCompiler) WriteStmtSelect(exp *ast.SelectStmt) error {
731
- // This is our implementation of the select statement, which will use Promise.race
732
- // to achieve the same semantics as Go's select statement.
733
-
734
- // Variable to track whether we have a default case
735
- hasDefault := false
736
-
737
- // Start the selectStatement call and the array literal
738
- c.tsw.WriteLiterally("await $.selectStatement(")
739
- c.tsw.WriteLine("[") // Put bracket on new line
740
- c.tsw.Indent(1)
741
-
742
- // For each case clause, generate a SelectCase object directly into the array literal
743
- for i, stmt := range exp.Body.List {
744
- if commClause, ok := stmt.(*ast.CommClause); ok {
745
- if commClause.Comm == nil {
746
- // This is a default case
747
- hasDefault = true
748
- // Add a SelectCase object for the default case with a special ID
749
- c.tsw.WriteLiterally("{") // Start object literal
750
- c.tsw.Indent(1)
751
- c.tsw.WriteLine("")
752
- c.tsw.WriteLiterally("id: -1,") // Special ID for default case
753
- c.tsw.WriteLine("")
754
- c.tsw.WriteLiterally("isSend: false,") // Default case is neither send nor receive, but needs a value
755
- c.tsw.WriteLine("")
756
- c.tsw.WriteLiterally("channel: null,") // No channel for default case
757
- c.tsw.WriteLine("")
758
- c.tsw.WriteLiterally("onSelected: async (result) => {") // Mark as async because case body might contain await
759
- c.tsw.Indent(1)
760
- c.tsw.WriteLine("")
761
- // Write the case body
762
- for _, bodyStmt := range commClause.Body {
763
- if err := c.WriteStmt(bodyStmt); err != nil {
764
- return fmt.Errorf("failed to write statement in select default case body (onSelected): %w", err)
765
- }
766
- }
767
- c.tsw.Indent(-1)
768
- c.tsw.WriteLine("}") // Close onSelected handler
769
- c.tsw.Indent(-1)
770
- c.tsw.WriteLiterally("},") // Close SelectCase object and add comma
771
- c.tsw.WriteLine("")
772
-
773
- continue
774
- }
775
-
776
- // Generate a unique ID for this case
777
- caseID := i
831
+ // WriteStmtLabeled handles labeled statements (ast.LabeledStmt), such as "label: statement".
832
+ // In TypeScript, this translates to "label: statement" directly.
833
+ func (c *GoToTSCompiler) WriteStmtLabeled(stmt *ast.LabeledStmt) error {
834
+ // Write the label name followed by a colon
835
+ c.tsw.WriteLiterally(stmt.Label.Name)
836
+ c.tsw.WriteLiterally(": ")
778
837
 
779
- // Start writing the SelectCase object
780
- c.tsw.WriteLiterally("{") // Start object literal
781
- c.tsw.Indent(1)
782
- c.tsw.WriteLine("")
783
- c.tsw.WriteLiterallyf("id: %d,", caseID)
784
- c.tsw.WriteLine("")
785
-
786
- // Handle different types of comm statements
787
- switch comm := commClause.Comm.(type) {
788
- case *ast.AssignStmt:
789
- // This is a receive operation with assignment: case v := <-ch: or case v, ok := <-ch:
790
- if len(comm.Rhs) == 1 {
791
- if unaryExpr, ok := comm.Rhs[0].(*ast.UnaryExpr); ok && unaryExpr.Op == token.ARROW {
792
- // It's a receive operation
793
- c.tsw.WriteLiterally("isSend: false,")
794
- c.tsw.WriteLine("")
795
- c.tsw.WriteLiterally("channel: ")
796
- if err := c.WriteValueExpr(unaryExpr.X); err != nil { // The channel expression
797
- return fmt.Errorf("failed to write channel expression in select receive case: %w", err)
798
- }
799
- c.tsw.WriteLiterally(",")
800
- c.tsw.WriteLine("")
801
- } else {
802
- c.tsw.WriteCommentLinef("unhandled RHS in select assignment case: %T", comm.Rhs[0])
803
- }
804
- } else {
805
- c.tsw.WriteCommentLinef("unhandled RHS count in select assignment case: %d", len(comm.Rhs))
806
- }
807
- case *ast.ExprStmt:
808
- // This is a simple receive: case <-ch:
809
- if unaryExpr, ok := comm.X.(*ast.UnaryExpr); ok && unaryExpr.Op == token.ARROW {
810
- c.tsw.WriteLiterally("isSend: false,")
811
- c.tsw.WriteLine("")
812
- c.tsw.WriteLiterally("channel: ")
813
- if err := c.WriteValueExpr(unaryExpr.X); err != nil { // The channel expression
814
- return fmt.Errorf("failed to write channel expression in select receive case: %w", err)
815
- }
816
- c.tsw.WriteLiterally(",")
817
- c.tsw.WriteLine("")
818
- } else {
819
- c.tsw.WriteCommentLinef("unhandled expression in select case: %T", comm.X)
820
- }
821
- case *ast.SendStmt:
822
- // This is a send operation: case ch <- v:
823
- c.tsw.WriteLiterally("isSend: true,")
824
- c.tsw.WriteLine("")
825
- c.tsw.WriteLiterally("channel: ")
826
- if err := c.WriteValueExpr(comm.Chan); err != nil { // The channel expression
827
- return fmt.Errorf("failed to write channel expression in select send case: %w", err)
828
- }
829
- c.tsw.WriteLiterally(",")
830
- c.tsw.WriteLine("")
831
- c.tsw.WriteLiterally("value: ")
832
- if err := c.WriteValueExpr(comm.Value); err != nil { // The value expression
833
- return fmt.Errorf("failed to write value expression in select send case: %w", err)
834
- }
835
- c.tsw.WriteLiterally(",")
836
- c.tsw.WriteLine("")
837
- default:
838
- c.tsw.WriteCommentLinef("unhandled comm statement in select case: %T", comm)
839
- }
840
-
841
- // Add the onSelected handler to execute the case body after the select resolves
842
- c.tsw.WriteLiterally("onSelected: async (result) => {") // Mark as async because case body might contain await
843
- c.tsw.Indent(1)
844
- c.tsw.WriteLine("")
845
-
846
- // Handle assignment for channel receives if needed (inside the onSelected handler)
847
- if assignStmt, ok := commClause.Comm.(*ast.AssignStmt); ok {
848
- // This is a receive operation with assignment
849
- if len(assignStmt.Lhs) == 1 {
850
- // Simple receive: case v := <-ch:
851
- valIdent, ok := assignStmt.Lhs[0].(*ast.Ident)
852
- if ok && valIdent.Name != "_" { // Check for blank identifier
853
- c.tsw.WriteLiterally("const ")
854
- c.WriteIdent(valIdent, false)
855
- c.tsw.WriteLiterally(" = result.value")
856
- c.tsw.WriteLine("")
857
- }
858
- } else if len(assignStmt.Lhs) == 2 {
859
- // Receive with ok: case v, ok := <-ch:
860
- valIdent, valOk := assignStmt.Lhs[0].(*ast.Ident)
861
- okIdent, okOk := assignStmt.Lhs[1].(*ast.Ident)
862
-
863
- if valOk && valIdent.Name != "_" {
864
- c.tsw.WriteLiterally("const ")
865
- c.WriteIdent(valIdent, false)
866
- c.tsw.WriteLiterally(" = result.value")
867
- c.tsw.WriteLine("")
868
- }
869
-
870
- if okOk && okIdent.Name != "_" {
871
- c.tsw.WriteLiterally("const ")
872
- c.WriteIdent(okIdent, false)
873
- c.tsw.WriteLiterally(" = result.ok")
874
- c.tsw.WriteLine("")
875
- }
876
- }
877
- }
878
- // Note: Simple receive (case <-ch:) and send (case ch <- v:) don't require assignment here,
879
- // as the operation was already performed by selectReceive/selectSend and the result is in 'result'.
880
-
881
- // Write the case body
882
- for _, bodyStmt := range commClause.Body {
883
- if err := c.WriteStmt(bodyStmt); err != nil {
884
- return fmt.Errorf("failed to write statement in select case body (onSelected): %w", err)
885
- }
886
- }
887
-
888
- c.tsw.Indent(-1)
889
- c.tsw.WriteLine("}") // Close onSelected handler
890
- c.tsw.Indent(-1)
891
- c.tsw.WriteLiterally("},") // Close SelectCase object and add comma
892
- c.tsw.WriteLine("")
893
-
894
- } else {
895
- c.tsw.WriteCommentLinef("unknown statement in select body: %T", stmt)
896
- }
838
+ // Write the labeled statement
839
+ if err := c.WriteStmt(stmt.Stmt); err != nil {
840
+ return fmt.Errorf("failed to write labeled statement: %w", err)
897
841
  }
898
842
 
899
- // Close the array literal and the selectStatement call
900
- c.tsw.Indent(-1)
901
- c.tsw.WriteLiterally("], ")
902
- c.tsw.WriteLiterallyf("%t", hasDefault)
903
- c.tsw.WriteLiterally(")")
904
- c.tsw.WriteLine("")
905
-
906
843
  return nil
907
844
  }