goscript 0.0.22 → 0.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cmd/goscript/cmd_compile.go +2 -2
- package/compiler/analysis.go +229 -51
- package/compiler/assignment.go +6 -1
- package/compiler/compiler.go +41 -18
- package/compiler/compiler_test.go +36 -8
- package/compiler/composite-lit.go +25 -10
- package/compiler/decl.go +36 -0
- package/compiler/expr-call.go +116 -60
- package/compiler/expr-selector.go +22 -2
- package/compiler/expr-type.go +131 -4
- package/compiler/expr-value.go +7 -37
- package/compiler/expr.go +247 -12
- package/compiler/field.go +3 -3
- package/compiler/lit.go +34 -2
- package/compiler/primitive.go +8 -2
- package/compiler/spec-struct.go +137 -6
- package/compiler/spec-value.go +50 -18
- package/compiler/spec.go +12 -3
- package/compiler/stmt-assign.go +29 -1
- package/compiler/stmt-range.go +9 -11
- package/compiler/stmt-select.go +211 -0
- package/compiler/stmt-type-switch.go +147 -0
- package/compiler/stmt.go +129 -244
- package/compiler/type-assert.go +125 -379
- package/compiler/type.go +187 -125
- package/package.json +5 -5
- package/builtin/builtin.go +0 -11
- package/builtin/builtin.ts +0 -2379
- package/dist/builtin/builtin.d.ts +0 -513
- package/dist/builtin/builtin.js +0 -1686
- package/dist/builtin/builtin.js.map +0 -1
package/compiler/stmt.go
CHANGED
|
@@ -33,7 +33,6 @@ import (
|
|
|
33
33
|
func (c *GoToTSCompiler) WriteStmt(a ast.Stmt) error {
|
|
34
34
|
switch exp := a.(type) {
|
|
35
35
|
case *ast.BlockStmt:
|
|
36
|
-
// WriteStmtBlock does not currently return an error, assuming it's safe for now.
|
|
37
36
|
if err := c.WriteStmtBlock(exp, false); err != nil {
|
|
38
37
|
return fmt.Errorf("failed to write block statement: %w", err)
|
|
39
38
|
}
|
|
@@ -73,7 +72,6 @@ func (c *GoToTSCompiler) WriteStmt(a ast.Stmt) error {
|
|
|
73
72
|
return fmt.Errorf("failed to write declaration statement: %w", err)
|
|
74
73
|
}
|
|
75
74
|
case *ast.ForStmt:
|
|
76
|
-
// WriteStmtFor does not currently return an error, assuming it's safe for now.
|
|
77
75
|
if err := c.WriteStmtFor(exp); err != nil {
|
|
78
76
|
return fmt.Errorf("failed to write for statement: %w", err)
|
|
79
77
|
}
|
|
@@ -83,7 +81,6 @@ func (c *GoToTSCompiler) WriteStmt(a ast.Stmt) error {
|
|
|
83
81
|
return fmt.Errorf("failed to write range statement: %w", err)
|
|
84
82
|
}
|
|
85
83
|
case *ast.SwitchStmt:
|
|
86
|
-
// WriteStmtSwitch does not currently return an error, assuming it's safe for now.
|
|
87
84
|
if err := c.WriteStmtSwitch(exp); err != nil {
|
|
88
85
|
return fmt.Errorf("failed to write switch statement: %w", err)
|
|
89
86
|
}
|
|
@@ -108,8 +105,12 @@ func (c *GoToTSCompiler) WriteStmt(a ast.Stmt) error {
|
|
|
108
105
|
if err := c.WriteStmtBranch(exp); err != nil {
|
|
109
106
|
return fmt.Errorf("failed to write branch statement: %w", err)
|
|
110
107
|
}
|
|
108
|
+
case *ast.TypeSwitchStmt:
|
|
109
|
+
if err := c.WriteStmtTypeSwitch(exp); err != nil {
|
|
110
|
+
return fmt.Errorf("failed to write type switch statement: %w", err)
|
|
111
|
+
}
|
|
111
112
|
default:
|
|
112
|
-
return errors.Errorf("unknown statement:
|
|
113
|
+
return errors.Errorf("unknown statement: %#v\n", a)
|
|
113
114
|
}
|
|
114
115
|
return nil
|
|
115
116
|
}
|
|
@@ -172,17 +173,17 @@ func (c *GoToTSCompiler) WriteStmtBranch(stmt *ast.BranchStmt) error {
|
|
|
172
173
|
}
|
|
173
174
|
|
|
174
175
|
// WriteStmtGo translates a Go statement (`ast.GoStmt`) into its TypeScript equivalent.
|
|
175
|
-
// It handles `go func(){...}()` and `go
|
|
176
|
+
// It handles `go func(){...}()`, `go namedFunc(args)`, and `go x.Method(args)`.
|
|
176
177
|
func (c *GoToTSCompiler) WriteStmtGo(exp *ast.GoStmt) error {
|
|
177
178
|
// Handle goroutine statement
|
|
178
179
|
// Translate 'go func() { ... }()' to 'queueMicrotask(() => { ... compiled body ... })'
|
|
179
|
-
|
|
180
|
-
// The call expression's function is the function literal
|
|
181
180
|
callExpr := exp.Call
|
|
182
|
-
|
|
181
|
+
|
|
182
|
+
switch fun := callExpr.Fun.(type) {
|
|
183
|
+
case *ast.FuncLit:
|
|
183
184
|
// For function literals, we need to check if the function literal itself is async
|
|
184
185
|
// This happens during analysis in analysisVisitor.Visit for FuncLit nodes
|
|
185
|
-
isAsync := c.analysis.IsFuncLitAsync(
|
|
186
|
+
isAsync := c.analysis.IsFuncLitAsync(fun)
|
|
186
187
|
if isAsync {
|
|
187
188
|
c.tsw.WriteLiterally("queueMicrotask(async () => ")
|
|
188
189
|
} else {
|
|
@@ -190,18 +191,18 @@ func (c *GoToTSCompiler) WriteStmtGo(exp *ast.GoStmt) error {
|
|
|
190
191
|
}
|
|
191
192
|
|
|
192
193
|
// Compile the function literal's body directly
|
|
193
|
-
if err := c.WriteStmtBlock(
|
|
194
|
+
if err := c.WriteStmtBlock(fun.Body, true); err != nil {
|
|
194
195
|
return fmt.Errorf("failed to write goroutine function literal body: %w", err)
|
|
195
196
|
}
|
|
196
197
|
|
|
197
198
|
c.tsw.WriteLine(")") // Close the queueMicrotask statement
|
|
198
199
|
|
|
199
|
-
|
|
200
|
+
case *ast.Ident:
|
|
200
201
|
// Handle named functions: go namedFunc(args)
|
|
201
202
|
// Get the object for this function
|
|
202
|
-
obj := c.pkg.TypesInfo.Uses[
|
|
203
|
+
obj := c.pkg.TypesInfo.Uses[fun]
|
|
203
204
|
if obj == nil {
|
|
204
|
-
return errors.Errorf("could not find object for function: %s",
|
|
205
|
+
return errors.Errorf("could not find object for function: %s", fun.Name)
|
|
205
206
|
}
|
|
206
207
|
|
|
207
208
|
// Check if the function is async
|
|
@@ -221,7 +222,7 @@ func (c *GoToTSCompiler) WriteStmtGo(exp *ast.GoStmt) error {
|
|
|
221
222
|
}
|
|
222
223
|
|
|
223
224
|
// Write the function name
|
|
224
|
-
c.tsw.WriteLiterally(
|
|
225
|
+
c.tsw.WriteLiterally(fun.Name)
|
|
225
226
|
|
|
226
227
|
// Write the function arguments
|
|
227
228
|
c.tsw.WriteLiterally("(")
|
|
@@ -238,7 +239,62 @@ func (c *GoToTSCompiler) WriteStmtGo(exp *ast.GoStmt) error {
|
|
|
238
239
|
|
|
239
240
|
c.tsw.Indent(-1)
|
|
240
241
|
c.tsw.WriteLine("})") // Close the queueMicrotask callback and the statement
|
|
241
|
-
|
|
242
|
+
case *ast.SelectorExpr:
|
|
243
|
+
// Handle selector expressions: go x.Method(args)
|
|
244
|
+
// Get the object for the selected method
|
|
245
|
+
obj := c.pkg.TypesInfo.Uses[fun.Sel]
|
|
246
|
+
if obj == nil {
|
|
247
|
+
return errors.Errorf("could not find object for selected method: %s", fun.Sel.Name)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Check if the function is async
|
|
251
|
+
isAsync := c.analysis.IsAsyncFunc(obj)
|
|
252
|
+
if isAsync {
|
|
253
|
+
c.tsw.WriteLiterally("queueMicrotask(async () => {")
|
|
254
|
+
} else {
|
|
255
|
+
c.tsw.WriteLiterally("queueMicrotask(() => {")
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
c.tsw.Indent(1)
|
|
259
|
+
c.tsw.WriteLine("")
|
|
260
|
+
|
|
261
|
+
// Write the function call, using await if the function is async
|
|
262
|
+
if isAsync {
|
|
263
|
+
c.tsw.WriteLiterally("await ")
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Write the selector expression (e.g., f.Bar)
|
|
267
|
+
// Note: callExpr.Fun is the *ast.SelectorExpr itself
|
|
268
|
+
// For method calls, we need to add null assertion since Go would panic on nil receiver
|
|
269
|
+
if selectorExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
|
|
270
|
+
if err := c.WriteValueExpr(selectorExpr.X); err != nil {
|
|
271
|
+
return fmt.Errorf("failed to write selector base expression in goroutine: %w", err)
|
|
272
|
+
}
|
|
273
|
+
// Add null assertion for method calls - Go would panic if receiver is nil
|
|
274
|
+
c.tsw.WriteLiterally("!.")
|
|
275
|
+
c.WriteIdent(selectorExpr.Sel, true)
|
|
276
|
+
} else {
|
|
277
|
+
if err := c.WriteValueExpr(callExpr.Fun); err != nil {
|
|
278
|
+
return fmt.Errorf("failed to write selector expression in goroutine: %w", err)
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Write the function arguments
|
|
283
|
+
c.tsw.WriteLiterally("(")
|
|
284
|
+
for i, arg := range callExpr.Args {
|
|
285
|
+
if i != 0 {
|
|
286
|
+
c.tsw.WriteLiterally(", ")
|
|
287
|
+
}
|
|
288
|
+
if err := c.WriteValueExpr(arg); err != nil {
|
|
289
|
+
return fmt.Errorf("failed to write argument %d in goroutine selector function call: %w", i, err)
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
c.tsw.WriteLiterally(")")
|
|
293
|
+
c.tsw.WriteLine("")
|
|
294
|
+
|
|
295
|
+
c.tsw.Indent(-1)
|
|
296
|
+
c.tsw.WriteLine("})") // Close the queueMicrotask callback and the statement
|
|
297
|
+
default:
|
|
242
298
|
return errors.Errorf("unhandled goroutine function type: %T", callExpr.Fun)
|
|
243
299
|
}
|
|
244
300
|
return nil
|
|
@@ -258,12 +314,12 @@ func (c *GoToTSCompiler) WriteStmtGo(exp *ast.GoStmt) error {
|
|
|
258
314
|
func (c *GoToTSCompiler) WriteStmtExpr(exp *ast.ExprStmt) error {
|
|
259
315
|
// Handle simple channel receive used as a statement (<-ch)
|
|
260
316
|
if unaryExpr, ok := exp.X.(*ast.UnaryExpr); ok && unaryExpr.Op == token.ARROW {
|
|
261
|
-
// Translate <-ch to await ch
|
|
262
|
-
c.tsw.WriteLiterally("await ")
|
|
317
|
+
// Translate <-ch to await $.chanRecv(ch)
|
|
318
|
+
c.tsw.WriteLiterally("await $.chanRecv(")
|
|
263
319
|
if err := c.WriteValueExpr(unaryExpr.X); err != nil { // Channel expression
|
|
264
320
|
return fmt.Errorf("failed to write channel expression in receive statement: %w", err)
|
|
265
321
|
}
|
|
266
|
-
c.tsw.WriteLiterally("
|
|
322
|
+
c.tsw.WriteLiterally(")") // Use chanRecv() as the value is discarded
|
|
267
323
|
c.tsw.WriteLine("")
|
|
268
324
|
return nil
|
|
269
325
|
}
|
|
@@ -315,12 +371,12 @@ func (c *GoToTSCompiler) WriteStmtExpr(exp *ast.ExprStmt) error {
|
|
|
315
371
|
// channel send operations are asynchronous in the TypeScript model.
|
|
316
372
|
// The statement is terminated with a newline.
|
|
317
373
|
func (c *GoToTSCompiler) WriteStmtSend(exp *ast.SendStmt) error {
|
|
318
|
-
// Translate ch <- value to await ch
|
|
319
|
-
c.tsw.WriteLiterally("await ")
|
|
374
|
+
// Translate ch <- value to await $.chanSend(ch, value)
|
|
375
|
+
c.tsw.WriteLiterally("await $.chanSend(")
|
|
320
376
|
if err := c.WriteValueExpr(exp.Chan); err != nil { // The channel expression
|
|
321
377
|
return fmt.Errorf("failed to write channel expression in send statement: %w", err)
|
|
322
378
|
}
|
|
323
|
-
c.tsw.WriteLiterally("
|
|
379
|
+
c.tsw.WriteLiterally(", ")
|
|
324
380
|
if err := c.WriteValueExpr(exp.Value); err != nil { // The value expression
|
|
325
381
|
return fmt.Errorf("failed to write value expression in send statement: %w", err)
|
|
326
382
|
}
|
|
@@ -346,16 +402,19 @@ func (c *GoToTSCompiler) WriteStmtSend(exp *ast.SendStmt) error {
|
|
|
346
402
|
// The function aims to produce idiomatic TypeScript `if/else if/else` structures.
|
|
347
403
|
func (s *GoToTSCompiler) WriteStmtIf(exp *ast.IfStmt) error {
|
|
348
404
|
if exp.Init != nil {
|
|
349
|
-
s.tsw.WriteLiterally("{")
|
|
350
|
-
s.tsw.
|
|
405
|
+
s.tsw.WriteLiterally("{") // Write opening brace
|
|
406
|
+
s.tsw.WriteLine("") // Add newline immediately after opening brace
|
|
407
|
+
s.tsw.Indent(1) // Indent for the initializer
|
|
351
408
|
|
|
352
|
-
if err := s.WriteStmt(exp.Init); err != nil {
|
|
409
|
+
if err := s.WriteStmt(exp.Init); err != nil { // Write the initializer
|
|
353
410
|
return err
|
|
354
411
|
}
|
|
355
412
|
|
|
413
|
+
// This defer handles closing the synthetic block for the initializer
|
|
356
414
|
defer func() {
|
|
357
415
|
s.tsw.Indent(-1)
|
|
358
|
-
s.tsw.WriteLiterally("}")
|
|
416
|
+
s.tsw.WriteLiterally("}") // Write the closing brace at the now-correct indent level
|
|
417
|
+
s.tsw.WriteLine("") // Ensure a newline *after* this '}', critical for preventing '}}'
|
|
359
418
|
}()
|
|
360
419
|
}
|
|
361
420
|
|
|
@@ -406,21 +465,51 @@ func (s *GoToTSCompiler) WriteStmtIf(exp *ast.IfStmt) error {
|
|
|
406
465
|
// The statement is terminated with a newline.
|
|
407
466
|
func (c *GoToTSCompiler) WriteStmtReturn(exp *ast.ReturnStmt) error {
|
|
408
467
|
c.tsw.WriteLiterally("return ")
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
468
|
+
|
|
469
|
+
// Check if it's a bare named return
|
|
470
|
+
nodeInfo := c.analysis.NodeData[exp]
|
|
471
|
+
if nodeInfo != nil && nodeInfo.IsBareReturn {
|
|
472
|
+
var namedReturns []string
|
|
473
|
+
if nodeInfo.EnclosingFuncDecl != nil {
|
|
474
|
+
if obj := c.pkg.TypesInfo.ObjectOf(nodeInfo.EnclosingFuncDecl.Name); obj != nil {
|
|
475
|
+
if funcInfo := c.analysis.FunctionData[obj]; funcInfo != nil {
|
|
476
|
+
namedReturns = funcInfo.NamedReturns
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
} else if nodeInfo.EnclosingFuncLit != nil {
|
|
480
|
+
if funcInfo := c.analysis.FuncLitData[nodeInfo.EnclosingFuncLit]; funcInfo != nil {
|
|
481
|
+
namedReturns = funcInfo.NamedReturns
|
|
482
|
+
}
|
|
415
483
|
}
|
|
416
|
-
|
|
417
|
-
|
|
484
|
+
|
|
485
|
+
if len(namedReturns) > 0 {
|
|
486
|
+
c.tsw.WriteLiterally("[")
|
|
487
|
+
for i, name := range namedReturns {
|
|
488
|
+
if i != 0 {
|
|
489
|
+
c.tsw.WriteLiterally(", ")
|
|
490
|
+
}
|
|
491
|
+
c.tsw.WriteLiterally(name)
|
|
492
|
+
}
|
|
493
|
+
c.tsw.WriteLiterally("]")
|
|
494
|
+
}
|
|
495
|
+
} else {
|
|
496
|
+
// Handle explicit return values
|
|
497
|
+
if len(exp.Results) > 1 {
|
|
498
|
+
c.tsw.WriteLiterally("[")
|
|
499
|
+
}
|
|
500
|
+
for i, res := range exp.Results {
|
|
501
|
+
if i != 0 {
|
|
502
|
+
c.tsw.WriteLiterally(", ")
|
|
503
|
+
}
|
|
504
|
+
if err := c.WriteValueExpr(res); err != nil { // Return results are values
|
|
505
|
+
return err
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
if len(exp.Results) > 1 {
|
|
509
|
+
c.tsw.WriteLiterally("]")
|
|
418
510
|
}
|
|
419
511
|
}
|
|
420
|
-
|
|
421
|
-
c.tsw.WriteLiterally("]")
|
|
422
|
-
}
|
|
423
|
-
c.tsw.WriteLine("") // Remove semicolon
|
|
512
|
+
c.tsw.WriteLine("")
|
|
424
513
|
return nil
|
|
425
514
|
}
|
|
426
515
|
|
|
@@ -464,7 +553,7 @@ func (c *GoToTSCompiler) WriteStmtBlock(exp *ast.BlockStmt, suppressNewline bool
|
|
|
464
553
|
for _, stmt := range exp.List {
|
|
465
554
|
if deferStmt, ok := stmt.(*ast.DeferStmt); ok {
|
|
466
555
|
if funcLit, ok := deferStmt.Call.Fun.(*ast.FuncLit); ok {
|
|
467
|
-
if c.analysis.
|
|
556
|
+
if c.analysis.IsFuncLitAsync(funcLit) {
|
|
468
557
|
hasAsyncDefer = true
|
|
469
558
|
break
|
|
470
559
|
}
|
|
@@ -530,7 +619,6 @@ func (c *GoToTSCompiler) WriteStmtBlock(exp *ast.BlockStmt, suppressNewline bool
|
|
|
530
619
|
start = file.Line(cg.Pos())
|
|
531
620
|
}
|
|
532
621
|
writeBlank(lastLine, start)
|
|
533
|
-
// WriteDoc does not currently return an error, assuming it's safe for now.
|
|
534
622
|
c.WriteDoc(cg) // WriteDoc will handle the actual comment text
|
|
535
623
|
if file != nil && cg.End().IsValid() {
|
|
536
624
|
lastLine = file.Line(cg.End())
|
|
@@ -567,7 +655,6 @@ func (c *GoToTSCompiler) WriteStmtBlock(exp *ast.BlockStmt, suppressNewline bool
|
|
|
567
655
|
// only emit if it follows the last content
|
|
568
656
|
if start > lastLine {
|
|
569
657
|
writeBlank(lastLine, start)
|
|
570
|
-
// WriteDoc does not currently return an error, assuming it's safe for now.
|
|
571
658
|
c.WriteDoc(cg)
|
|
572
659
|
if file != nil && cg.End().IsValid() {
|
|
573
660
|
lastLine = file.Line(cg.End())
|
|
@@ -634,7 +721,6 @@ func (c *GoToTSCompiler) WriteStmtSwitch(exp *ast.SwitchStmt) error {
|
|
|
634
721
|
// Handle case clauses
|
|
635
722
|
for _, stmt := range exp.Body.List {
|
|
636
723
|
if caseClause, ok := stmt.(*ast.CaseClause); ok {
|
|
637
|
-
// WriteCaseClause does not currently return an error, assuming it's safe for now.
|
|
638
724
|
if err := c.WriteCaseClause(caseClause); err != nil {
|
|
639
725
|
return fmt.Errorf("failed to write case clause in switch statement: %w", err)
|
|
640
726
|
}
|
|
@@ -667,7 +753,7 @@ func (c *GoToTSCompiler) WriteStmtDefer(exp *ast.DeferStmt) error {
|
|
|
667
753
|
// Determine if the deferred call is to an async function literal using analysis
|
|
668
754
|
isAsyncDeferred := false
|
|
669
755
|
if funcLit, ok := exp.Call.Fun.(*ast.FuncLit); ok {
|
|
670
|
-
isAsyncDeferred = c.analysis.
|
|
756
|
+
isAsyncDeferred = c.analysis.IsFuncLitAsync(funcLit)
|
|
671
757
|
}
|
|
672
758
|
|
|
673
759
|
// Set async prefix based on pre-computed async status
|
|
@@ -696,6 +782,7 @@ func (c *GoToTSCompiler) WriteStmtDefer(exp *ast.DeferStmt) error {
|
|
|
696
782
|
if err := c.WriteValueExpr(exp.Call); err != nil {
|
|
697
783
|
return fmt.Errorf("failed to write deferred call: %w", err)
|
|
698
784
|
}
|
|
785
|
+
c.tsw.WriteLine("")
|
|
699
786
|
}
|
|
700
787
|
|
|
701
788
|
c.tsw.Indent(-1)
|
|
@@ -703,205 +790,3 @@ func (c *GoToTSCompiler) WriteStmtDefer(exp *ast.DeferStmt) error {
|
|
|
703
790
|
|
|
704
791
|
return nil
|
|
705
792
|
}
|
|
706
|
-
|
|
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
|
|
778
|
-
|
|
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
|
-
}
|
|
897
|
-
}
|
|
898
|
-
|
|
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
|
-
return nil
|
|
907
|
-
}
|