goscript 0.0.84 → 0.1.0

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 (188) hide show
  1. package/README.md +13 -1
  2. package/cmd/goscript/cmd_compile.go +70 -69
  3. package/cmd/goscript/cmd_compile_test.go +79 -0
  4. package/cmd/goscript/main.go +10 -5
  5. package/compiler/compile-request.go +218 -0
  6. package/compiler/compiler.go +16 -1336
  7. package/compiler/compliance_test.go +196 -0
  8. package/compiler/config.go +6 -13
  9. package/compiler/diagnostic.go +70 -0
  10. package/compiler/index.test.ts +28 -28
  11. package/compiler/index.ts +40 -72
  12. package/compiler/lowered-program.go +132 -0
  13. package/compiler/lowering.go +3576 -0
  14. package/compiler/override-registry.go +422 -0
  15. package/compiler/override-registry_test.go +207 -0
  16. package/compiler/package-graph.go +231 -0
  17. package/compiler/package-graph_test.go +281 -0
  18. package/compiler/result.go +13 -0
  19. package/compiler/runtime-contract.go +279 -0
  20. package/compiler/runtime-contract_test.go +90 -0
  21. package/compiler/semantic-model-types.go +110 -0
  22. package/compiler/semantic-model.go +922 -0
  23. package/compiler/semantic-model_test.go +416 -0
  24. package/compiler/service.go +133 -0
  25. package/compiler/skeleton_test.go +1145 -0
  26. package/compiler/typescript-emitter.go +663 -0
  27. package/compiler/wasm/compile.go +2 -3
  28. package/compiler/wasm/compile_test.go +29 -0
  29. package/compiler/wasm_api.go +10 -159
  30. package/dist/compiler/index.d.ts +1 -3
  31. package/dist/compiler/index.js +31 -55
  32. package/dist/compiler/index.js.map +1 -1
  33. package/dist/gs/builtin/builtin.d.ts +13 -0
  34. package/dist/gs/builtin/builtin.js +23 -0
  35. package/dist/gs/builtin/builtin.js.map +1 -1
  36. package/dist/gs/builtin/channel.d.ts +3 -3
  37. package/dist/gs/builtin/channel.js.map +1 -1
  38. package/dist/gs/builtin/hostio.d.ts +15 -1
  39. package/dist/gs/builtin/hostio.js +134 -49
  40. package/dist/gs/builtin/hostio.js.map +1 -1
  41. package/dist/gs/builtin/index.d.ts +1 -0
  42. package/dist/gs/builtin/index.js +1 -0
  43. package/dist/gs/builtin/index.js.map +1 -1
  44. package/dist/gs/builtin/slice.d.ts +1 -1
  45. package/dist/gs/builtin/slice.js.map +1 -1
  46. package/dist/gs/builtin/type.d.ts +11 -0
  47. package/dist/gs/builtin/type.js +55 -1
  48. package/dist/gs/builtin/type.js.map +1 -1
  49. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  50. package/dist/gs/bytes/bytes.gs.js.map +1 -1
  51. package/dist/gs/bytes/reader.gs.js.map +1 -1
  52. package/dist/gs/context/context.js.map +1 -1
  53. package/dist/gs/crypto/rand/index.d.ts +5 -0
  54. package/dist/gs/crypto/rand/index.js +77 -0
  55. package/dist/gs/crypto/rand/index.js.map +1 -0
  56. package/dist/gs/encoding/json/index.d.ts +3 -0
  57. package/dist/gs/encoding/json/index.js +160 -0
  58. package/dist/gs/encoding/json/index.js.map +1 -0
  59. package/dist/gs/fmt/fmt.js.map +1 -1
  60. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -1
  61. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +1 -1
  62. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  63. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js.map +1 -1
  64. package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
  65. package/dist/gs/github.com/pkg/errors/stack.js.map +1 -1
  66. package/dist/gs/go/scanner/index.d.ts +29 -0
  67. package/dist/gs/go/scanner/index.js +120 -0
  68. package/dist/gs/go/scanner/index.js.map +1 -0
  69. package/dist/gs/go/token/index.d.ts +31 -0
  70. package/dist/gs/go/token/index.js +82 -0
  71. package/dist/gs/go/token/index.js.map +1 -0
  72. package/dist/gs/internal/abi/index.js.map +1 -1
  73. package/dist/gs/io/fs/fs.js.map +1 -1
  74. package/dist/gs/io/fs/readdir.js.map +1 -1
  75. package/dist/gs/io/fs/readfile.js.map +1 -1
  76. package/dist/gs/io/fs/stat.js.map +1 -1
  77. package/dist/gs/io/fs/sub.js.map +1 -1
  78. package/dist/gs/io/io.js.map +1 -1
  79. package/dist/gs/os/dir_unix.gs.js.map +1 -1
  80. package/dist/gs/os/error.gs.js +2 -4
  81. package/dist/gs/os/error.gs.js.map +1 -1
  82. package/dist/gs/os/exec.gs.js.map +1 -1
  83. package/dist/gs/os/exec_posix.gs.js.map +1 -1
  84. package/dist/gs/os/rawconn_js.gs.js.map +1 -1
  85. package/dist/gs/os/root_js.gs.js.map +1 -1
  86. package/dist/gs/os/tempfile.gs.js +66 -9
  87. package/dist/gs/os/tempfile.gs.js.map +1 -1
  88. package/dist/gs/os/types.gs.js.map +1 -1
  89. package/dist/gs/os/types_js.gs.js +9 -9
  90. package/dist/gs/os/types_js.gs.js.map +1 -1
  91. package/dist/gs/os/types_unix.gs.js.map +1 -1
  92. package/dist/gs/path/filepath/match.js.map +1 -1
  93. package/dist/gs/path/match.js.map +1 -1
  94. package/dist/gs/path/path.js.map +1 -1
  95. package/dist/gs/reflect/index.d.ts +2 -2
  96. package/dist/gs/reflect/index.js +1 -1
  97. package/dist/gs/reflect/index.js.map +1 -1
  98. package/dist/gs/reflect/map.js.map +1 -1
  99. package/dist/gs/reflect/type.d.ts +2 -1
  100. package/dist/gs/reflect/type.js +85 -14
  101. package/dist/gs/reflect/type.js.map +1 -1
  102. package/dist/gs/reflect/types.js.map +1 -1
  103. package/dist/gs/reflect/visiblefields.js.map +1 -1
  104. package/dist/gs/runtime/runtime.js.map +1 -1
  105. package/dist/gs/sort/sort.gs.js.map +1 -1
  106. package/dist/gs/strconv/atoi.gs.js.map +1 -1
  107. package/dist/gs/strconv/quote.gs.js.map +1 -1
  108. package/dist/gs/strings/builder.js.map +1 -1
  109. package/dist/gs/strings/reader.js.map +1 -1
  110. package/dist/gs/strings/replace.js.map +1 -1
  111. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  112. package/dist/gs/sync/atomic/value.gs.js.map +1 -1
  113. package/dist/gs/sync/sync.d.ts +1 -0
  114. package/dist/gs/sync/sync.js +12 -0
  115. package/dist/gs/sync/sync.js.map +1 -1
  116. package/dist/gs/time/time.js.map +1 -1
  117. package/dist/gs/unicode/unicode.js.map +1 -1
  118. package/go.mod +2 -2
  119. package/gs/builtin/builtin.ts +27 -0
  120. package/gs/builtin/hostio.test.ts +177 -0
  121. package/gs/builtin/hostio.ts +171 -56
  122. package/gs/builtin/index.ts +1 -0
  123. package/gs/builtin/runtime-contract.test.ts +230 -0
  124. package/gs/builtin/type.ts +84 -1
  125. package/gs/crypto/rand/index.test.ts +32 -0
  126. package/gs/crypto/rand/index.ts +90 -0
  127. package/gs/crypto/rand/meta.json +5 -0
  128. package/gs/encoding/json/index.test.ts +65 -0
  129. package/gs/encoding/json/index.ts +186 -0
  130. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +23 -0
  131. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +3 -1
  132. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/meta.json +3 -1
  133. package/gs/go/scanner/index.test.ts +50 -0
  134. package/gs/go/scanner/index.ts +157 -0
  135. package/gs/go/token/index.test.ts +21 -0
  136. package/gs/go/token/index.ts +120 -0
  137. package/gs/os/file_unix_js.test.ts +50 -0
  138. package/gs/os/meta.json +1 -2
  139. package/gs/os/tempfile.gs.test.ts +85 -0
  140. package/gs/os/tempfile.gs.ts +71 -11
  141. package/gs/os/types_js.gs.ts +9 -9
  142. package/gs/reflect/index.ts +1 -1
  143. package/gs/reflect/type.ts +106 -17
  144. package/gs/reflect/typefor.test.ts +75 -0
  145. package/gs/sync/sync.test.ts +24 -0
  146. package/gs/sync/sync.ts +12 -0
  147. package/package.json +13 -13
  148. package/compiler/analysis.go +0 -3475
  149. package/compiler/analysis_test.go +0 -338
  150. package/compiler/assignment.go +0 -580
  151. package/compiler/builtin_test.go +0 -92
  152. package/compiler/code-writer.go +0 -115
  153. package/compiler/compiler_test.go +0 -149
  154. package/compiler/composite-lit.go +0 -779
  155. package/compiler/config_test.go +0 -62
  156. package/compiler/constraint.go +0 -86
  157. package/compiler/decl.go +0 -801
  158. package/compiler/expr-call-async.go +0 -188
  159. package/compiler/expr-call-builtins.go +0 -208
  160. package/compiler/expr-call-helpers.go +0 -382
  161. package/compiler/expr-call-make.go +0 -318
  162. package/compiler/expr-call-type-conversion.go +0 -520
  163. package/compiler/expr-call.go +0 -413
  164. package/compiler/expr-selector.go +0 -343
  165. package/compiler/expr-star.go +0 -82
  166. package/compiler/expr-type.go +0 -442
  167. package/compiler/expr-value.go +0 -89
  168. package/compiler/expr.go +0 -773
  169. package/compiler/field.go +0 -183
  170. package/compiler/gs_dependencies_test.go +0 -298
  171. package/compiler/lit.go +0 -322
  172. package/compiler/output.go +0 -72
  173. package/compiler/primitive.go +0 -149
  174. package/compiler/protobuf.go +0 -697
  175. package/compiler/sanitize.go +0 -100
  176. package/compiler/spec-struct.go +0 -995
  177. package/compiler/spec-value.go +0 -540
  178. package/compiler/spec.go +0 -725
  179. package/compiler/stmt-assign.go +0 -664
  180. package/compiler/stmt-for.go +0 -266
  181. package/compiler/stmt-range.go +0 -475
  182. package/compiler/stmt-select.go +0 -262
  183. package/compiler/stmt-type-switch.go +0 -147
  184. package/compiler/stmt.go +0 -1308
  185. package/compiler/type-assert.go +0 -386
  186. package/compiler/type-info.go +0 -156
  187. package/compiler/type-utils.go +0 -207
  188. package/compiler/type.go +0 -892
@@ -1,262 +0,0 @@
1
- package compiler
2
-
3
- import (
4
- "fmt"
5
- "go/ast"
6
- "go/token"
7
-
8
- "github.com/pkg/errors"
9
- )
10
-
11
- // caseEndsWithReturn checks if a case body ends with a return statement
12
- func (c *GoToTSCompiler) caseEndsWithReturn(body []ast.Stmt) bool {
13
- if len(body) == 0 {
14
- return false
15
- }
16
-
17
- // Check if the last statement is a return statement
18
- lastStmt := body[len(body)-1]
19
- _, isReturn := lastStmt.(*ast.ReturnStmt)
20
- return isReturn
21
- }
22
-
23
- // WriteStmtSelect translates a Go `select` statement into an asynchronous
24
- // TypeScript operation using the `$.selectStatement` runtime helper.
25
- // Go's `select` provides non-deterministic choice over channel operations.
26
- // This is emulated by constructing an array of `SelectCase` objects, one for
27
- // each `case` in the Go `select`, and passing it to `$.selectStatement`.
28
- //
29
- // Each `SelectCase` object includes:
30
- // - `id`: A unique identifier for the case.
31
- // - `isSend`: `true` for send operations (`case ch <- val:`), `false` for receives.
32
- // - `channel`: The TypeScript channel object.
33
- // - `value` (for sends): The value being sent.
34
- // - `onSelected: async (result) => { ... }`: A callback executed when this case
35
- // is chosen. `result` contains `{ value, ok }` for receives.
36
- // - Inside `onSelected`, assignments for receive operations (e.g., `v := <-ch`,
37
- // `v, ok := <-ch`) are handled by declaring/assigning variables from `result.value`
38
- // and `result.ok`.
39
- // - The original Go case body is then translated within this callback.
40
- //
41
- // A `default` case in Go `select` is translated to a `SelectCase` with `id: -1`
42
- // and its body in the `onSelected` handler. The `$.selectStatement` helper
43
- // is informed if a default case exists.
44
- // The entire `$.selectStatement(...)` call is `await`ed because channel
45
- // operations are asynchronous in the TypeScript model.
46
- func (c *GoToTSCompiler) WriteStmtSelect(exp *ast.SelectStmt) error {
47
- // This is our implementation of the select statement, which will use Promise.race
48
- // to achieve the same semantics as Go's select statement.
49
-
50
- // Variable to track whether we have a default case
51
- hasDefault := false
52
-
53
- // Analyze if all cases end with return statements
54
- allCasesReturn := true
55
- for _, stmt := range exp.Body.List {
56
- if commClause, ok := stmt.(*ast.CommClause); ok {
57
- if commClause.Comm == nil {
58
- // Default case - check if it ends with return
59
- if !c.caseEndsWithReturn(commClause.Body) {
60
- allCasesReturn = false
61
- }
62
- } else {
63
- // Regular case - check if it ends with return
64
- if !c.caseEndsWithReturn(commClause.Body) {
65
- allCasesReturn = false
66
- }
67
- }
68
- }
69
- }
70
-
71
- // Generate unique variable names for this select statement
72
- selectID := c.getDeterministicID(exp.Pos()) // Use deterministic position-based ID
73
-
74
- // Start the selectStatement call and the array literal
75
- c.tsw.WriteLiterallyf("const [_select_has_return_%s, _select_value_%s] = await $.selectStatement(", selectID, selectID)
76
- c.tsw.WriteLine("[") // Put bracket on new line
77
- c.tsw.Indent(1)
78
-
79
- // For each case clause, generate a SelectCase object directly into the array literal
80
- for i, stmt := range exp.Body.List {
81
- if commClause, ok := stmt.(*ast.CommClause); ok {
82
- if commClause.Comm == nil {
83
- // This is a default case
84
- hasDefault = true
85
- // Add a SelectCase object for the default case with a special ID
86
- c.tsw.WriteLiterally("{") // Start object literal
87
- c.tsw.Indent(1)
88
- c.tsw.WriteLine("")
89
- c.tsw.WriteLiterally("id: -1,") // Special ID for default case
90
- c.tsw.WriteLine("")
91
- c.tsw.WriteLiterally("isSend: false,") // Default case is neither send nor receive, but needs a value
92
- c.tsw.WriteLine("")
93
- c.tsw.WriteLiterally("channel: null,") // No channel for default case
94
- c.tsw.WriteLine("")
95
- c.tsw.WriteLiterally("onSelected: async (result) => {") // Mark as async because case body might contain await
96
- c.tsw.Indent(1)
97
- c.tsw.WriteLine("")
98
- // Write the case body
99
- for _, bodyStmt := range commClause.Body {
100
- if err := c.WriteStmt(bodyStmt); err != nil {
101
- return fmt.Errorf("failed to write statement in select default case body (onSelected): %w", err)
102
- }
103
- }
104
- c.tsw.Indent(-1)
105
- c.tsw.WriteLine("}") // Close onSelected handler
106
- c.tsw.Indent(-1)
107
- c.tsw.WriteLiterally("},") // Close SelectCase object and add comma
108
- c.tsw.WriteLine("")
109
-
110
- continue
111
- }
112
-
113
- // Generate a unique ID for this case
114
- caseID := i
115
-
116
- // Start writing the SelectCase object
117
- c.tsw.WriteLiterally("{") // Start object literal
118
- c.tsw.Indent(1)
119
- c.tsw.WriteLine("")
120
- c.tsw.WriteLiterallyf("id: %d,", caseID)
121
- c.tsw.WriteLine("")
122
-
123
- // Handle different types of comm statements
124
- switch comm := commClause.Comm.(type) {
125
- case *ast.AssignStmt:
126
- // This is a receive operation with assignment: case v := <-ch: or case v, ok := <-ch:
127
- if len(comm.Rhs) == 1 {
128
- if unaryExpr, ok := comm.Rhs[0].(*ast.UnaryExpr); ok && unaryExpr.Op == token.ARROW {
129
- // It's a receive operation
130
- c.tsw.WriteLiterally("isSend: false,")
131
- c.tsw.WriteLine("")
132
- c.tsw.WriteLiterally("channel: ")
133
- if err := c.WriteValueExpr(unaryExpr.X); err != nil { // The channel expression
134
- return fmt.Errorf("failed to write channel expression in select receive case: %w", err)
135
- }
136
- c.tsw.WriteLiterally(",")
137
- c.tsw.WriteLine("")
138
- } else {
139
- c.tsw.WriteCommentLinef("unhandled RHS in select assignment case: %T", comm.Rhs[0])
140
- }
141
- } else {
142
- c.tsw.WriteCommentLinef("unhandled RHS count in select assignment case: %d", len(comm.Rhs))
143
- }
144
- case *ast.ExprStmt:
145
- // This is a simple receive: case <-ch:
146
- if unaryExpr, ok := comm.X.(*ast.UnaryExpr); ok && unaryExpr.Op == token.ARROW {
147
- c.tsw.WriteLiterally("isSend: false,")
148
- c.tsw.WriteLine("")
149
- c.tsw.WriteLiterally("channel: ")
150
- if err := c.WriteValueExpr(unaryExpr.X); err != nil { // The channel expression
151
- return fmt.Errorf("failed to write channel expression in select receive case: %w", err)
152
- }
153
- c.tsw.WriteLiterally(",")
154
- c.tsw.WriteLine("")
155
- } else {
156
- c.tsw.WriteCommentLinef("unhandled expression in select case: %T", comm.X)
157
- }
158
- case *ast.SendStmt:
159
- // This is a send operation: case ch <- v:
160
- c.tsw.WriteLiterally("isSend: true,")
161
- c.tsw.WriteLine("")
162
- c.tsw.WriteLiterally("channel: ")
163
- if err := c.WriteValueExpr(comm.Chan); err != nil { // The channel expression
164
- return fmt.Errorf("failed to write channel expression in select send case: %w", err)
165
- }
166
- c.tsw.WriteLiterally(",")
167
- c.tsw.WriteLine("")
168
- c.tsw.WriteLiterally("value: ")
169
- if err := c.WriteValueExpr(comm.Value); err != nil { // The value expression
170
- return fmt.Errorf("failed to write value expression in select send case: %w", err)
171
- }
172
- c.tsw.WriteLiterally(",")
173
- c.tsw.WriteLine("")
174
- default:
175
- c.tsw.WriteCommentLinef("unhandled comm statement in select case: %T", comm)
176
- }
177
-
178
- // Add the onSelected handler to execute the case body after the select resolves
179
- c.tsw.WriteLiterally("onSelected: async (result) => {") // Mark as async because case body might contain await
180
- c.tsw.Indent(1)
181
- c.tsw.WriteLine("")
182
-
183
- // Handle assignment for channel receives if needed (inside the onSelected handler)
184
- if assignStmt, ok := commClause.Comm.(*ast.AssignStmt); ok {
185
- // This is a receive operation with assignment
186
- if len(assignStmt.Lhs) == 1 {
187
- // Simple receive: case v := <-ch:
188
- valIdent, ok := assignStmt.Lhs[0].(*ast.Ident)
189
- if ok && valIdent.Name != "_" { // Check for blank identifier
190
- c.tsw.WriteLiterally("const ")
191
- c.WriteIdent(valIdent, false)
192
- c.tsw.WriteLiterally(" = result.value")
193
- c.tsw.WriteLine("")
194
- }
195
- } else if len(assignStmt.Lhs) == 2 {
196
- // Receive with ok: case v, ok := <-ch:
197
- valIdent, valOk := assignStmt.Lhs[0].(*ast.Ident)
198
- okIdent, okOk := assignStmt.Lhs[1].(*ast.Ident)
199
-
200
- if valOk && valIdent.Name != "_" {
201
- c.tsw.WriteLiterally("const ")
202
- c.WriteIdent(valIdent, false)
203
- c.tsw.WriteLiterally(" = result.value")
204
- c.tsw.WriteLine("")
205
- }
206
-
207
- if okOk && okIdent.Name != "_" {
208
- c.tsw.WriteLiterally("const ")
209
- c.WriteIdent(okIdent, false)
210
- c.tsw.WriteLiterally(" = result.ok")
211
- c.tsw.WriteLine("")
212
- }
213
- }
214
- }
215
- // Note: Simple receive (case <-ch:) and send (case ch <- v:) don't require assignment here,
216
- // as the operation was already performed by selectReceive/selectSend and the result is in 'result'.
217
-
218
- // Write the case body
219
- for _, bodyStmt := range commClause.Body {
220
- if err := c.WriteStmt(bodyStmt); err != nil {
221
- return fmt.Errorf("failed to write statement in select case body (onSelected): %w", err)
222
- }
223
- }
224
-
225
- c.tsw.Indent(-1)
226
- c.tsw.WriteLine("}") // Close onSelected handler
227
- c.tsw.Indent(-1)
228
- c.tsw.WriteLiterally("},") // Close SelectCase object and add comma
229
- c.tsw.WriteLine("")
230
-
231
- } else {
232
- return errors.Errorf("unknown statement in select body: %T", stmt)
233
- }
234
- }
235
-
236
- // Close the array literal and the selectStatement call
237
- c.tsw.Indent(-1)
238
- c.tsw.WriteLiterally("], ")
239
- c.tsw.WriteLiterallyf("%t", hasDefault)
240
- c.tsw.WriteLiterally(")")
241
- c.tsw.WriteLine("")
242
-
243
- // Add code to handle the return value from selectStatement
244
- c.tsw.WriteLiterallyf("if (_select_has_return_%s) {", selectID)
245
- c.tsw.WriteLine("")
246
- c.tsw.Indent(1)
247
- c.tsw.WriteLiterallyf("return _select_value_%s!", selectID)
248
- c.tsw.WriteLine("")
249
- c.tsw.Indent(-1)
250
- c.tsw.WriteLine("}")
251
-
252
- // If all cases return, add a TypeScript-satisfying fallback return
253
- if allCasesReturn {
254
- c.tsw.WriteLine("// All cases should return, this fallback should never execute")
255
- c.tsw.WriteLine("throw new Error('Unexpected: select statement did not return when all cases should return')")
256
- } else {
257
- c.tsw.WriteLiterallyf("// If _select_has_return_%s is false, continue execution", selectID)
258
- c.tsw.WriteLine("")
259
- }
260
-
261
- return nil
262
- }
@@ -1,147 +0,0 @@
1
- package compiler
2
-
3
- import (
4
- "fmt"
5
- "go/ast"
6
-
7
- "github.com/pkg/errors"
8
- )
9
-
10
- // WriteStmtTypeSwitch translates a Go `type switch` statement (`ast.TypeSwitchStmt`)
11
- // into its TypeScript equivalent using the `$.typeSwitch` helper.
12
- func (c *GoToTSCompiler) WriteStmtTypeSwitch(stmt *ast.TypeSwitchStmt) error {
13
- // Outer block for scoping Init variable
14
- if stmt.Init != nil {
15
- c.tsw.WriteLine("{")
16
- c.tsw.Indent(1)
17
- if err := c.WriteStmt(stmt.Init); err != nil {
18
- return fmt.Errorf("failed to write type switch init statement: %w", err)
19
- }
20
- }
21
-
22
- // Extract the subject expression and case variable identifier
23
- var subjectExpr ast.Expr
24
- var caseVarIdent *ast.Ident // The variable in 'v := x.(type)'
25
-
26
- switch assignNode := stmt.Assign.(type) {
27
- case *ast.AssignStmt: // v := x.(type)
28
- if len(assignNode.Lhs) != 1 || len(assignNode.Rhs) != 1 {
29
- return errors.Errorf("TypeSwitchStmt AssignStmt: expected 1 LHS and 1 RHS, got %d and %d", len(assignNode.Lhs), len(assignNode.Rhs))
30
- }
31
- ident, ok := assignNode.Lhs[0].(*ast.Ident)
32
- if !ok {
33
- return errors.Errorf("TypeSwitchStmt AssignStmt LHS is not *ast.Ident: %T", assignNode.Lhs[0])
34
- }
35
- caseVarIdent = ident
36
- typeAssert, ok := assignNode.Rhs[0].(*ast.TypeAssertExpr)
37
- if !ok {
38
- return errors.Errorf("TypeSwitchStmt AssignStmt RHS is not *ast.TypeAssertExpr: %T", assignNode.Rhs[0])
39
- }
40
- if typeAssert.Type != nil {
41
- return errors.Errorf("TypeSwitchStmt AssignStmt TypeAssertExpr.Type is not nil")
42
- }
43
- subjectExpr = typeAssert.X
44
- case *ast.ExprStmt: // x.(type)
45
- typeAssert, ok := assignNode.X.(*ast.TypeAssertExpr)
46
- if !ok {
47
- return errors.Errorf("TypeSwitchStmt ExprStmt.X is not *ast.TypeAssertExpr: %T", assignNode.X)
48
- }
49
- if typeAssert.Type != nil {
50
- return errors.Errorf("TypeSwitchStmt ExprStmt TypeAssertExpr.Type is not nil")
51
- }
52
- subjectExpr = typeAssert.X
53
- default:
54
- return errors.Errorf("unknown Assign type in TypeSwitchStmt: %T", stmt.Assign)
55
- }
56
-
57
- // Build the array of case configurations for $.typeSwitch
58
- c.tsw.WriteLiterally("$.typeSwitch(")
59
- if err := c.WriteValueExpr(subjectExpr); err != nil {
60
- c.tsw.Indent(-1)
61
- c.tsw.WriteLine("} // End TypeSwitchStmt due to error in subject")
62
- return fmt.Errorf("failed to write subject expression in type switch: %w", err)
63
- }
64
-
65
- // case list
66
- c.tsw.WriteLiterally(", [")
67
-
68
- stmtBodyList := stmt.Body.List
69
- var defaultCaseBody []ast.Stmt
70
-
71
- for i, caseClauseStmt := range stmtBodyList {
72
- caseClause, ok := caseClauseStmt.(*ast.CaseClause)
73
- if !ok {
74
- return errors.Errorf("unexpected statement in TypeSwitchStmt Body: not *ast.CaseClause but %T", caseClauseStmt)
75
- }
76
-
77
- if len(caseClause.List) == 0 { // Default case
78
- defaultCaseBody = caseClause.Body
79
- continue // Process default case after type cases
80
- }
81
-
82
- // Type case(s)
83
- if i != 0 {
84
- c.tsw.WriteLiterally(",")
85
- c.tsw.WriteLine("")
86
- }
87
-
88
- c.tsw.WriteLiterally("{ types: [")
89
- for j, typeExpr := range caseClause.List {
90
- if j > 0 {
91
- c.tsw.WriteLiterally(", ")
92
- }
93
- c.writeTypeDescription(typeExpr) // Descriptor for $.is or $.typeAssert
94
- }
95
- c.tsw.WriteLiterally("], body: (")
96
-
97
- // Add case variable if it exists and is not '_'
98
- if caseVarIdent != nil && caseVarIdent.Name != "_" {
99
- c.WriteIdent(caseVarIdent, false) // isDeclaration = false for the parameter
100
- // Note: TypeScript type inference should handle the parameter type based on the case type(s)
101
- }
102
-
103
- c.tsw.WriteLiterally(") => {")
104
-
105
- caseClauseBody := caseClause.Body
106
- if len(caseClauseBody) != 0 {
107
- c.tsw.Indent(1)
108
- c.tsw.WriteLine("")
109
- }
110
-
111
- for _, bodyStmt := range caseClauseBody {
112
- if err := c.WriteStmt(bodyStmt); err != nil {
113
- return fmt.Errorf("failed to write statement in type switch case body: %w", err)
114
- }
115
- }
116
-
117
- if len(caseClauseBody) != 0 {
118
- c.tsw.Indent(-1)
119
- }
120
- c.tsw.WriteLiterally("}") // Close case body function
121
- c.tsw.WriteLiterally("}") // Close case object
122
- }
123
-
124
- c.tsw.WriteLiterally("]") // Close cases array
125
-
126
- // Add default case function if it exists
127
- if len(defaultCaseBody) != 0 {
128
- c.tsw.WriteLiterally(", () => {")
129
- c.tsw.Indent(1)
130
- c.tsw.WriteLine("")
131
- for _, bodyStmt := range defaultCaseBody {
132
- if err := c.WriteStmt(bodyStmt); err != nil {
133
- return fmt.Errorf("failed to write statement in type switch default case body: %w", err)
134
- }
135
- }
136
- c.tsw.Indent(-1)
137
- c.tsw.WriteLiterally("}") // Close default case function
138
- }
139
-
140
- c.tsw.WriteLine(")") // Close $.typeSwitch call
141
- if stmt.Init != nil {
142
- c.tsw.Indent(-1)
143
- c.tsw.WriteLine("}") // Close outer block
144
- }
145
-
146
- return nil
147
- }