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.
- package/README.md +1 -1
- package/cmd/goscript/cmd_compile.go +3 -3
- package/compiler/analysis.go +302 -182
- package/compiler/analysis_test.go +220 -0
- package/compiler/assignment.go +42 -43
- package/compiler/builtin_test.go +102 -0
- package/compiler/compiler.go +117 -29
- package/compiler/compiler_test.go +36 -8
- package/compiler/composite-lit.go +133 -53
- package/compiler/config.go +7 -3
- package/compiler/config_test.go +6 -33
- package/compiler/decl.go +36 -0
- package/compiler/expr-call.go +116 -60
- package/compiler/expr-selector.go +88 -43
- package/compiler/expr-star.go +57 -65
- package/compiler/expr-type.go +132 -5
- package/compiler/expr-value.go +8 -38
- package/compiler/expr.go +326 -30
- package/compiler/field.go +3 -3
- package/compiler/lit.go +34 -2
- package/compiler/primitive.go +19 -12
- package/compiler/spec-struct.go +140 -9
- package/compiler/spec-value.go +119 -41
- package/compiler/spec.go +21 -6
- package/compiler/stmt-assign.go +65 -3
- package/compiler/stmt-for.go +11 -0
- package/compiler/stmt-range.go +119 -11
- package/compiler/stmt-select.go +211 -0
- package/compiler/stmt-type-switch.go +147 -0
- package/compiler/stmt.go +175 -238
- package/compiler/type-assert.go +125 -379
- package/compiler/type.go +216 -129
- package/dist/gs/builtin/builtin.js +37 -0
- package/dist/gs/builtin/builtin.js.map +1 -0
- package/dist/gs/builtin/channel.js +471 -0
- package/dist/gs/builtin/channel.js.map +1 -0
- package/dist/gs/builtin/defer.js +54 -0
- package/dist/gs/builtin/defer.js.map +1 -0
- package/dist/gs/builtin/io.js +15 -0
- package/dist/gs/builtin/io.js.map +1 -0
- package/dist/gs/builtin/map.js +44 -0
- package/dist/gs/builtin/map.js.map +1 -0
- package/dist/gs/builtin/slice.js +799 -0
- package/dist/gs/builtin/slice.js.map +1 -0
- package/dist/gs/builtin/type.js +745 -0
- package/dist/gs/builtin/type.js.map +1 -0
- package/dist/gs/builtin/varRef.js +14 -0
- package/dist/gs/builtin/varRef.js.map +1 -0
- package/dist/gs/context/context.js +55 -0
- package/dist/gs/context/context.js.map +1 -0
- package/dist/gs/context/index.js +2 -0
- package/dist/gs/context/index.js.map +1 -0
- package/dist/gs/runtime/index.js +2 -0
- package/dist/gs/runtime/index.js.map +1 -0
- package/dist/gs/runtime/runtime.js +158 -0
- package/dist/gs/runtime/runtime.js.map +1 -0
- package/dist/gs/time/index.js +2 -0
- package/dist/gs/time/index.js.map +1 -0
- package/dist/gs/time/time.js +115 -0
- package/dist/gs/time/time.js.map +1 -0
- package/package.json +7 -6
- 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
|
@@ -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:
|
|
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
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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[
|
|
209
|
+
obj := c.pkg.TypesInfo.Uses[fun]
|
|
203
210
|
if obj == nil {
|
|
204
|
-
return errors.Errorf("could not find object for function: %s",
|
|
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(
|
|
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
|
-
|
|
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
|
|
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("
|
|
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
|
|
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("
|
|
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.
|
|
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
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
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
|
-
|
|
417
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
//
|
|
708
|
-
// TypeScript
|
|
709
|
-
|
|
710
|
-
//
|
|
711
|
-
|
|
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
|
-
|
|
780
|
-
|
|
781
|
-
|
|
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
|
}
|