goscript 0.0.2

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 (160) hide show
  1. package/.aider-prompt +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +427 -0
  4. package/builtin/builtin.ts +507 -0
  5. package/cmd/goscript/cmd_compile.go +59 -0
  6. package/cmd/goscript/main.go +23 -0
  7. package/compiler/compile.go +183 -0
  8. package/compiler/compile_comment.go +41 -0
  9. package/compiler/compile_decls.go +72 -0
  10. package/compiler/compile_expr.go +831 -0
  11. package/compiler/compile_field.go +89 -0
  12. package/compiler/compile_spec.go +256 -0
  13. package/compiler/compile_stmt.go +1509 -0
  14. package/compiler/compiler.go +81 -0
  15. package/compiler/config.go +32 -0
  16. package/compiler/context.go +9 -0
  17. package/compiler/file_compiler.go +80 -0
  18. package/compiler/output_path.go +31 -0
  19. package/compiler/pkg_compiler.go +73 -0
  20. package/compiler/writer.go +90 -0
  21. package/compliance/COMPLIANCE.md +133 -0
  22. package/compliance/compliance.go +313 -0
  23. package/compliance/compliance_test.go +57 -0
  24. package/compliance/tests/array_literal/array_literal.go +15 -0
  25. package/compliance/tests/array_literal/array_literal.gs.ts +19 -0
  26. package/compliance/tests/array_literal/expected.log +3 -0
  27. package/compliance/tests/async_basic/async_basic.go +26 -0
  28. package/compliance/tests/async_basic/async_basic.gs.ts +30 -0
  29. package/compliance/tests/async_basic/expected.log +1 -0
  30. package/compliance/tests/basic_arithmetic/basic_arithmetic.go +15 -0
  31. package/compliance/tests/basic_arithmetic/basic_arithmetic.gs.ts +19 -0
  32. package/compliance/tests/basic_arithmetic/expected.log +5 -0
  33. package/compliance/tests/boolean_logic/boolean_logic.go +13 -0
  34. package/compliance/tests/boolean_logic/boolean_logic.gs.ts +17 -0
  35. package/compliance/tests/boolean_logic/expected.log +3 -0
  36. package/compliance/tests/channel_basic/channel_basic.go +12 -0
  37. package/compliance/tests/channel_basic/channel_basic.gs.ts +18 -0
  38. package/compliance/tests/channel_basic/expected.log +1 -0
  39. package/compliance/tests/composite_literal_assignment/composite_literal_assignment.go +20 -0
  40. package/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.ts +27 -0
  41. package/compliance/tests/composite_literal_assignment/expected.log +2 -0
  42. package/compliance/tests/constants/constants.go +18 -0
  43. package/compliance/tests/constants/constants.gs.ts +22 -0
  44. package/compliance/tests/constants/expected.log +3 -0
  45. package/compliance/tests/copy_independence/copy_independence.go +29 -0
  46. package/compliance/tests/copy_independence/copy_independence.gs.ts +36 -0
  47. package/compliance/tests/copy_independence/expected.log +4 -0
  48. package/compliance/tests/float64/expected.log +6 -0
  49. package/compliance/tests/float64/float64.go +28 -0
  50. package/compliance/tests/float64/float64.gs.ts +32 -0
  51. package/compliance/tests/for_loop_basic/expected.log +5 -0
  52. package/compliance/tests/for_loop_basic/for_loop_basic.go +9 -0
  53. package/compliance/tests/for_loop_basic/for_loop_basic.gs.ts +13 -0
  54. package/compliance/tests/for_loop_condition_only/expected.log +5 -0
  55. package/compliance/tests/for_loop_condition_only/main.go +9 -0
  56. package/compliance/tests/for_loop_condition_only/main.gs.ts +13 -0
  57. package/compliance/tests/for_range/expected.log +9 -0
  58. package/compliance/tests/for_range/for_range.go +26 -0
  59. package/compliance/tests/for_range/for_range.gs.ts +45 -0
  60. package/compliance/tests/for_range_index_use/expected.log +6 -0
  61. package/compliance/tests/for_range_index_use/for_range_index_use.go +11 -0
  62. package/compliance/tests/for_range_index_use/for_range_index_use.gs.ts +18 -0
  63. package/compliance/tests/func_literal/expected.log +1 -0
  64. package/compliance/tests/func_literal/func_literal.go +10 -0
  65. package/compliance/tests/func_literal/func_literal.gs.ts +15 -0
  66. package/compliance/tests/function_call_result_assignment/expected.log +2 -0
  67. package/compliance/tests/function_call_result_assignment/function_call_result_assignment.go +24 -0
  68. package/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.ts +31 -0
  69. package/compliance/tests/if_statement/expected.log +1 -0
  70. package/compliance/tests/if_statement/if_statement.go +11 -0
  71. package/compliance/tests/if_statement/if_statement.gs.ts +15 -0
  72. package/compliance/tests/interface_to_interface_type_assertion/expected.log +1 -0
  73. package/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.go +30 -0
  74. package/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.ts +41 -0
  75. package/compliance/tests/interface_type_assertion/expected.log +1 -0
  76. package/compliance/tests/interface_type_assertion/interface_type_assertion.go +26 -0
  77. package/compliance/tests/interface_type_assertion/interface_type_assertion.gs.ts +36 -0
  78. package/compliance/tests/map_support/expected.log +13 -0
  79. package/compliance/tests/map_support/map_support.go +89 -0
  80. package/compliance/tests/map_support/map_support.gs.ts +102 -0
  81. package/compliance/tests/method_call_on_pointer_receiver/expected.log +1 -0
  82. package/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.go +19 -0
  83. package/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.ts +27 -0
  84. package/compliance/tests/method_call_on_pointer_via_value/expected.log +1 -0
  85. package/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.go +29 -0
  86. package/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.ts +38 -0
  87. package/compliance/tests/method_call_on_value_receiver/expected.log +1 -0
  88. package/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.go +16 -0
  89. package/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.ts +24 -0
  90. package/compliance/tests/method_call_on_value_via_pointer/expected.log +2 -0
  91. package/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.go +30 -0
  92. package/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.ts +38 -0
  93. package/compliance/tests/multiple_return_values/expected.log +6 -0
  94. package/compliance/tests/multiple_return_values/multiple_return_values.go +19 -0
  95. package/compliance/tests/multiple_return_values/multiple_return_values.gs.ts +23 -0
  96. package/compliance/tests/pointer_assignment_no_copy/expected.log +2 -0
  97. package/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.go +28 -0
  98. package/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.ts +35 -0
  99. package/compliance/tests/pointer_composite_literal_assignment/expected.log +3 -0
  100. package/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.go +23 -0
  101. package/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.ts +30 -0
  102. package/compliance/tests/pointer_deref_multiassign/expected.log +0 -0
  103. package/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.go +17 -0
  104. package/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.ts +27 -0
  105. package/compliance/tests/pointer_initialization/expected.log +1 -0
  106. package/compliance/tests/pointer_initialization/pointer_initialization.go +16 -0
  107. package/compliance/tests/pointer_initialization/pointer_initialization.gs.ts +22 -0
  108. package/compliance/tests/select_receive_on_closed_channel_no_default/expected.log +1 -0
  109. package/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.go +15 -0
  110. package/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.ts +31 -0
  111. package/compliance/tests/select_send_on_full_buffered_channel_with_default/expected.log +1 -0
  112. package/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.go +13 -0
  113. package/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.gs.ts +35 -0
  114. package/compliance/tests/select_statement/expected.log +9 -0
  115. package/compliance/tests/select_statement/select_statement.go +109 -0
  116. package/compliance/tests/select_statement/select_statement.gs.ts +239 -0
  117. package/compliance/tests/simple/expected.log +1 -0
  118. package/compliance/tests/simple/simple.go +5 -0
  119. package/compliance/tests/simple/simple.gs.ts +9 -0
  120. package/compliance/tests/simple_deref_assignment/expected.log +2 -0
  121. package/compliance/tests/simple_deref_assignment/simple_deref_assignment.go +19 -0
  122. package/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.ts +26 -0
  123. package/compliance/tests/slices/expected.log +7 -0
  124. package/compliance/tests/slices/slices.go +22 -0
  125. package/compliance/tests/slices/slices.gs.ts +26 -0
  126. package/compliance/tests/string_rune_conversion/expected.log +3 -0
  127. package/compliance/tests/string_rune_conversion/string_rune_conversion.go +16 -0
  128. package/compliance/tests/string_rune_conversion/string_rune_conversion.gs.ts +22 -0
  129. package/compliance/tests/struct_field_access/expected.log +2 -0
  130. package/compliance/tests/struct_field_access/struct_field_access.go +13 -0
  131. package/compliance/tests/struct_field_access/struct_field_access.gs.ts +20 -0
  132. package/compliance/tests/struct_value_init_clone/expected.log +5 -0
  133. package/compliance/tests/struct_value_init_clone/struct_value_init_clone.go +28 -0
  134. package/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.ts +35 -0
  135. package/compliance/tests/switch_statement/expected.log +14 -0
  136. package/compliance/tests/switch_statement/switch_statement.go +59 -0
  137. package/compliance/tests/switch_statement/switch_statement.gs.ts +85 -0
  138. package/compliance/tests/value_type_copy_behavior/expected.log +3 -0
  139. package/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.go +25 -0
  140. package/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.ts +34 -0
  141. package/design/DESIGN.md +599 -0
  142. package/example/simple/build.bash +10 -0
  143. package/example/simple/go.mod +23 -0
  144. package/example/simple/go.sum +39 -0
  145. package/example/simple/main.go +138 -0
  146. package/example/simple/main.gs.ts +133 -0
  147. package/example/simple/main.ts +3 -0
  148. package/example/simple/main_test.go +59 -0
  149. package/example/simple/main_tools.go +5 -0
  150. package/example/simple/package.json +7 -0
  151. package/example/simple/run.bash +6 -0
  152. package/example/simple/tsconfig.json +28 -0
  153. package/example/simple/yarn.lock +8 -0
  154. package/go.mod +22 -0
  155. package/go.sum +39 -0
  156. package/output/output.go +10 -0
  157. package/package.json +14 -0
  158. package/tsconfig.json +10 -0
  159. package/types/tokens.go +65 -0
  160. package/types/types.go +46 -0
@@ -0,0 +1,831 @@
1
+ package compiler
2
+
3
+ import (
4
+ "errors"
5
+ "fmt"
6
+ "go/ast"
7
+ "go/token"
8
+ gtypes "go/types"
9
+ "strconv"
10
+
11
+ gstypes "github.com/paralin/goscript/types"
12
+ )
13
+
14
+ // WriteTypeExpr writes an expression that represents a type.
15
+ func (c *GoToTSCompiler) WriteTypeExpr(a ast.Expr) {
16
+ switch exp := a.(type) {
17
+ case *ast.Ident:
18
+ c.WriteIdentType(exp)
19
+ case *ast.SelectorExpr:
20
+ if err := c.WriteSelectorExprType(exp); err != nil {
21
+ c.tsw.WriteCommentInline(fmt.Sprintf("error writing selector expr type: %v", err))
22
+ }
23
+ case *ast.StarExpr:
24
+ c.WriteStarExprType(exp)
25
+ case *ast.StructType:
26
+ c.WriteStructType(exp)
27
+ case *ast.InterfaceType:
28
+ c.WriteInterfaceType(exp)
29
+ case *ast.FuncType:
30
+ c.WriteFuncType(exp, false) // Function types are not async
31
+ case *ast.ArrayType:
32
+ // Translate [N]T to T[]
33
+ c.WriteTypeExpr(exp.Elt)
34
+ c.tsw.WriteLiterally("[]")
35
+ case *ast.MapType:
36
+ // Map<K,V> → TS object type { [key: K]: V }
37
+ c.tsw.WriteLiterally("{ [key: ")
38
+ c.WriteTypeExpr(exp.Key)
39
+ c.tsw.WriteLiterally("]: ")
40
+ c.WriteTypeExpr(exp.Value)
41
+ c.tsw.WriteLiterally(" }")
42
+ case *ast.ChanType:
43
+ // Translate channel types to goscript.Channel<T>
44
+ c.tsw.WriteLiterally("goscript.Channel<")
45
+ c.WriteTypeExpr(exp.Value) // Write the element type
46
+ c.tsw.WriteLiterally(">")
47
+ default:
48
+ c.tsw.WriteCommentLine(fmt.Sprintf("unhandled type expr: %T (map/chan support pending)", exp))
49
+ }
50
+ }
51
+
52
+ // WriteValueExpr writes an expression that represents a value.
53
+ func (c *GoToTSCompiler) WriteValueExpr(a ast.Expr) error {
54
+ switch exp := a.(type) {
55
+ case *ast.Ident:
56
+ c.WriteIdentValue(exp)
57
+ return nil
58
+ case *ast.SelectorExpr:
59
+ return c.WriteSelectorExprValue(exp)
60
+ case *ast.StarExpr:
61
+ return c.WriteStarExprValue(exp)
62
+ case *ast.CallExpr:
63
+ return c.WriteCallExpr(exp)
64
+ case *ast.UnaryExpr:
65
+ return c.WriteUnaryExprValue(exp)
66
+ case *ast.BinaryExpr:
67
+ return c.WriteBinaryExprValue(exp)
68
+ case *ast.BasicLit:
69
+ c.WriteBasicLitValue(exp)
70
+ return nil
71
+ case *ast.CompositeLit:
72
+ return c.WriteCompositeLitValue(exp)
73
+ case *ast.KeyValueExpr:
74
+ return c.WriteKeyValueExprValue(exp)
75
+ case *ast.IndexExpr:
76
+ // Handle map access: use Map.get() instead of brackets for reading values
77
+ if tv, ok := c.pkg.TypesInfo.Types[exp.X]; ok {
78
+ // Check if it's a map type
79
+ if _, isMap := tv.Type.Underlying().(*gtypes.Map); isMap {
80
+ if err := c.WriteValueExpr(exp.X); err != nil {
81
+ return err
82
+ }
83
+ c.tsw.WriteLiterally(".get(")
84
+ if err := c.WriteValueExpr(exp.Index); err != nil {
85
+ return err
86
+ }
87
+ // Note: For map access (reading), Go returns the zero value if the key doesn't exist.
88
+ // We need to handle this in TS. For now, default to 0 or null based on type.
89
+ // A more robust solution would involve checking the expected type of the context.
90
+ // For simplicity, let's add a comment indicating this might need refinement.
91
+ c.tsw.WriteLiterally(")") // No ?? 0 here, get() returns undefined if not found
92
+ // c.tsw.WriteCommentInline("map access might need zero value handling")
93
+ return nil
94
+ }
95
+ }
96
+
97
+ // Regular array/slice access: use brackets
98
+ if err := c.WriteValueExpr(exp.X); err != nil {
99
+ return err
100
+ }
101
+ c.tsw.WriteLiterally("[")
102
+ if err := c.WriteValueExpr(exp.Index); err != nil {
103
+ return err
104
+ }
105
+ c.tsw.WriteLiterally("]")
106
+ return nil
107
+ case *ast.ParenExpr:
108
+ // Translate (X) to (X)
109
+ c.tsw.WriteLiterally("(")
110
+ if err := c.WriteValueExpr(exp.X); err != nil {
111
+ return err
112
+ }
113
+ c.tsw.WriteLiterally(")")
114
+ return nil
115
+ case *ast.FuncLit:
116
+ return c.WriteFuncLitValue(exp)
117
+ // Add cases for SliceExpr etc.
118
+ default:
119
+ c.tsw.WriteCommentLine(fmt.Sprintf("unhandled value expr: %T", exp))
120
+ return nil
121
+ }
122
+ }
123
+
124
+ // --- Exported Node-Specific Writers ---
125
+
126
+ // WriteIdentType writes an identifier used as a type.
127
+ func (c *GoToTSCompiler) WriteIdentType(exp *ast.Ident) {
128
+ name := exp.Name
129
+ if tsname, ok := gstypes.GoBuiltinToTypescript(name); ok {
130
+ name = tsname
131
+ } else {
132
+ // Not a Go builtin. Could be a custom type in the current package,
133
+ // an imported type, or potentially an error.
134
+ // Robust checking requires type information.
135
+ if obj := exp.Obj; obj != nil && obj.Kind != ast.Typ {
136
+ c.tsw.WriteCommentInline(fmt.Sprintf("ident %q used as type? kind=%s", name, obj.Kind))
137
+ }
138
+
139
+ // TODO use type information to check
140
+
141
+ //else if obj == nil {
142
+ // If obj is nil, it might be a type from an import or undefined.
143
+ // Type checking pass should resolve this.
144
+ // c.tsw.WriteCommentInline(fmt.Sprintf("unresolved ident %q used as type", name))
145
+ //}
146
+
147
+ // Assume it's a valid custom type name for now.
148
+ }
149
+
150
+ c.tsw.WriteLiterally(name)
151
+ }
152
+
153
+ // WriteIdentValue writes an identifier used as a value (variable, function name).
154
+ func (c *GoToTSCompiler) WriteIdentValue(exp *ast.Ident) {
155
+ c.tsw.WriteLiterally(exp.Name)
156
+ }
157
+
158
+ // WriteSelectorExprType writes a selector expression used as a type (e.g., pkg.Type).
159
+ func (c *GoToTSCompiler) WriteSelectorExprType(exp *ast.SelectorExpr) error {
160
+ // Assuming X is a package identifier. Needs refinement with type info.
161
+ if err := c.WriteValueExpr(exp.X); err != nil { // Package name is treated as a value
162
+ return fmt.Errorf("failed to write selector expression package identifier: %w", err)
163
+ }
164
+ c.tsw.WriteLiterally(".")
165
+ c.WriteTypeExpr(exp.Sel) // The selected identifier is treated as a type
166
+ return nil
167
+ }
168
+
169
+ // WriteSelectorExprValue writes a selector expression used as a value (e.g., obj.Field).
170
+ func (c *GoToTSCompiler) WriteSelectorExprValue(exp *ast.SelectorExpr) error {
171
+ if err := c.WriteValueExpr(exp.X); err != nil {
172
+ return fmt.Errorf("failed to write selector expression object: %w", err)
173
+ }
174
+ c.tsw.WriteLiterally(".")
175
+ c.WriteIdentValue(exp.Sel)
176
+ return nil
177
+ }
178
+
179
+ // WriteStarExprType writes a pointer type (e.g., *MyStruct).
180
+ func (c *GoToTSCompiler) WriteStarExprType(exp *ast.StarExpr) {
181
+ // Map pointer types to T | null
182
+ c.WriteTypeExpr(exp.X)
183
+ c.tsw.WriteLiterally(" | null")
184
+ }
185
+
186
+ // WriteStarExprValue writes a pointer dereference value (e.g., *myVar).
187
+ func (c *GoToTSCompiler) WriteStarExprValue(exp *ast.StarExpr) error {
188
+ // Dereferencing a pointer in Go (*p) gets the value.
189
+ // In TS, if p is MyStruct | null, accessing the value means just using p.
190
+ // Cloning to emulate value semantics happens during assignment (see WriteStmtAssign).
191
+ if err := c.WriteValueExpr(exp.X); err != nil {
192
+ return fmt.Errorf("failed to write star expression operand: %w", err)
193
+ }
194
+ return nil
195
+ }
196
+
197
+ // WriteStructType writes a struct type definition.
198
+ func (c *GoToTSCompiler) WriteStructType(exp *ast.StructType) {
199
+ if exp.Fields == nil || exp.Fields.NumFields() == 0 {
200
+ c.tsw.WriteLiterally("{}")
201
+ return
202
+ }
203
+ c.WriteFieldList(exp.Fields, false) // false = not arguments
204
+ }
205
+
206
+ // WriteInterfaceType writes an interface type definition.
207
+ func (c *GoToTSCompiler) WriteInterfaceType(exp *ast.InterfaceType) {
208
+ if exp.Methods == nil || exp.Methods.NumFields() == 0 {
209
+ c.tsw.WriteLiterally("{}")
210
+ return
211
+ }
212
+ c.tsw.WriteLine("{")
213
+ c.tsw.Indent(1)
214
+ for _, method := range exp.Methods.List {
215
+ if len(method.Names) > 0 {
216
+ // Keep original Go casing for method names
217
+ methodName := method.Names[0]
218
+ c.WriteIdentValue(methodName)
219
+
220
+ // Method signature is a FuncType
221
+ if funcType, ok := method.Type.(*ast.FuncType); ok {
222
+ c.WriteFuncType(funcType, false) // Interface method signatures are not async
223
+ } else {
224
+ // Should not happen for valid interfaces, but handle defensively
225
+ c.tsw.WriteCommentInline("unexpected method type")
226
+ }
227
+ c.tsw.WriteLine(";")
228
+ } else {
229
+ // Embedded interface - write the type name
230
+ c.WriteTypeExpr(method.Type)
231
+ c.tsw.WriteLine("; // Embedded interface - requires manual merging or mixin in TS")
232
+ }
233
+ }
234
+ c.tsw.Indent(-1)
235
+ c.tsw.WriteLine("}")
236
+ }
237
+
238
+ // WriteFuncType writes a function type signature.
239
+ func (c *GoToTSCompiler) WriteFuncType(exp *ast.FuncType, isAsync bool) {
240
+ c.tsw.WriteLiterally("(")
241
+ c.WriteFieldList(exp.Params, true) // true = arguments
242
+ c.tsw.WriteLiterally(")")
243
+ if exp.Results != nil && len(exp.Results.List) > 0 {
244
+ // Use colon for return type annotation
245
+ c.tsw.WriteLiterally(": ")
246
+ if isAsync {
247
+ c.tsw.WriteLiterally("Promise<")
248
+ }
249
+ if len(exp.Results.List) == 1 && len(exp.Results.List[0].Names) == 0 {
250
+ // Single unnamed return type
251
+ c.WriteTypeExpr(exp.Results.List[0].Type)
252
+ } else {
253
+ // Multiple or named return types -> tuple
254
+ c.tsw.WriteLiterally("[")
255
+ for i, field := range exp.Results.List {
256
+ if i > 0 {
257
+ c.tsw.WriteLiterally(", ")
258
+ }
259
+ c.WriteTypeExpr(field.Type)
260
+ }
261
+ c.tsw.WriteLiterally("]")
262
+ }
263
+ if isAsync {
264
+ c.tsw.WriteLiterally(">")
265
+ }
266
+ } else {
267
+ // No return value -> void
268
+ if isAsync {
269
+ c.tsw.WriteLiterally(": Promise<void>")
270
+ } else {
271
+ c.tsw.WriteLiterally(": void")
272
+ }
273
+ }
274
+ }
275
+
276
+ // WriteCallExpr writes a function call.
277
+ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
278
+ expFun := exp.Fun
279
+
280
+ if funIdent, funIsIdent := expFun.(*ast.Ident); funIsIdent {
281
+ switch funIdent.String() {
282
+ case "println":
283
+ c.tsw.WriteLiterally("console.log(")
284
+ for i, arg := range exp.Args {
285
+ if i != 0 {
286
+ c.tsw.WriteLiterally(", ")
287
+ }
288
+ if err := c.WriteValueExpr(arg); err != nil {
289
+ return err
290
+ }
291
+ }
292
+ c.tsw.WriteLiterally(")")
293
+ return nil
294
+ case "len":
295
+ // Translate len(arg) to goscript.len(arg)
296
+ if len(exp.Args) == 1 {
297
+ c.tsw.WriteLiterally("goscript.len(")
298
+ if err := c.WriteValueExpr(exp.Args[0]); err != nil {
299
+ return err
300
+ }
301
+ c.tsw.WriteLiterally(")")
302
+ return nil // Handled len
303
+ }
304
+ return errors.New("unhandled len call with incorrect number of arguments")
305
+ case "cap":
306
+ // Translate cap(arg) to goscript.cap(arg)
307
+ if len(exp.Args) == 1 {
308
+ c.tsw.WriteLiterally("goscript.cap(")
309
+ if err := c.WriteValueExpr(exp.Args[0]); err != nil {
310
+ return err
311
+ }
312
+ c.tsw.WriteLiterally(")")
313
+ return nil // Handled cap
314
+ }
315
+ return errors.New("unhandled cap call with incorrect number of arguments")
316
+ case "delete":
317
+ // Translate delete(map, key) to goscript.deleteMapEntry(map, key)
318
+ if len(exp.Args) == 2 {
319
+ c.tsw.WriteLiterally("goscript.deleteMapEntry(")
320
+ if err := c.WriteValueExpr(exp.Args[0]); err != nil { // Map
321
+ return err
322
+ }
323
+ c.tsw.WriteLiterally(", ")
324
+ if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Key
325
+ return err
326
+ }
327
+ c.tsw.WriteLiterally(")")
328
+ return nil // Handled delete
329
+ }
330
+ return errors.New("unhandled delete call with incorrect number of arguments")
331
+ case "make":
332
+ // First check if we have a channel type
333
+ if typ := c.pkg.TypesInfo.TypeOf(exp.Args[0]); typ != nil {
334
+ if chanType, ok := typ.Underlying().(*gtypes.Chan); ok {
335
+ // Handle channel creation: make(chan T, bufferSize) or make(chan T)
336
+ c.tsw.WriteLiterally("goscript.makeChannel<")
337
+ c.WriteGoType(chanType.Elem())
338
+ c.tsw.WriteLiterally(">(")
339
+
340
+ // If buffer size is provided, add it
341
+ if len(exp.Args) >= 2 {
342
+ if err := c.WriteValueExpr(exp.Args[1]); err != nil {
343
+ return fmt.Errorf("failed to write buffer size in makeChannel: %w", err)
344
+ }
345
+ } else {
346
+ // Default to 0 (unbuffered channel)
347
+ c.tsw.WriteLiterally("0")
348
+ }
349
+
350
+ c.tsw.WriteLiterally(", ") // Add comma for zero value argument
351
+
352
+ // Write the zero value for the channel's element type
353
+ c.WriteZeroValueForType(chanType.Elem())
354
+
355
+ c.tsw.WriteLiterally(")")
356
+ return nil // Handled make for channel
357
+ }
358
+ }
359
+ // Handle make for slices: make([]T, len, cap) or make([]T, len)
360
+ if len(exp.Args) >= 1 {
361
+ // Handle map creation: make(map[K]V)
362
+ if mapType, ok := exp.Args[0].(*ast.MapType); ok {
363
+ c.tsw.WriteLiterally("goscript.makeMap(")
364
+
365
+ // Get key type
366
+ if keyType := c.pkg.TypesInfo.TypeOf(mapType.Key); keyType != nil {
367
+ // Use the underlying type for basic types
368
+ underlyingKeyType := keyType.Underlying()
369
+ c.tsw.WriteLiterally(fmt.Sprintf("%q", underlyingKeyType.String()))
370
+ } else {
371
+ // If type info is not available, this is an error condition
372
+ return errors.New("could not determine key type for makeMap")
373
+ }
374
+
375
+ c.tsw.WriteLiterally(", ")
376
+
377
+ // Get value type
378
+ if valueType := c.pkg.TypesInfo.TypeOf(mapType.Value); valueType != nil {
379
+ // Use the underlying type for basic types
380
+ underlyingValueType := valueType.Underlying()
381
+ c.tsw.WriteLiterally(fmt.Sprintf("%q", underlyingValueType.String()))
382
+ } else {
383
+ // If type info is not available, this is an error condition
384
+ return errors.New("could not determine value type for makeMap")
385
+ }
386
+
387
+ c.tsw.WriteLiterally(")")
388
+ return nil // Handled make for map
389
+ }
390
+
391
+ // Handle slice creation
392
+ if arrayType, ok := exp.Args[0].(*ast.ArrayType); ok {
393
+ c.tsw.WriteLiterally("goscript.makeSlice(")
394
+ // Get and write the string representation of the element type
395
+ if typ := c.pkg.TypesInfo.TypeOf(arrayType.Elt); typ != nil {
396
+ // Use the underlying type for basic types like int, string, etc.
397
+ underlyingType := typ.Underlying()
398
+ c.tsw.WriteLiterally(fmt.Sprintf("%q", underlyingType.String()))
399
+ } else {
400
+ // If type info is not available, this is an error condition for makeSlice
401
+ return errors.New("could not determine element type for makeSlice")
402
+ }
403
+ c.tsw.WriteLiterally(", ")
404
+ if len(exp.Args) >= 2 {
405
+ if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
406
+ return err
407
+ }
408
+ if len(exp.Args) == 3 {
409
+ c.tsw.WriteLiterally(", ")
410
+ if err := c.WriteValueExpr(exp.Args[2]); err != nil { // Capacity
411
+ return err
412
+ }
413
+ } else if len(exp.Args) > 3 {
414
+ return errors.New("makeSlice expects 2 or 3 arguments")
415
+ }
416
+ } else {
417
+ // If no length is provided, default to 0
418
+ c.tsw.WriteLiterally("0")
419
+ }
420
+ c.tsw.WriteLiterally(")")
421
+ return nil // Handled make for slice
422
+ }
423
+ }
424
+ // Fallthrough for unhandled make calls (e.g., channels)
425
+ return errors.New("unhandled make call")
426
+ case "string":
427
+ // Handle string() conversion, specifically for rune to string
428
+ if len(exp.Args) == 1 {
429
+ // Check if the argument is a rune (int32) or a call to rune()
430
+ arg := exp.Args[0]
431
+ innerCall, isCallExpr := arg.(*ast.CallExpr)
432
+
433
+ if isCallExpr {
434
+ // Check if it's a call to rune()
435
+ if innerFunIdent, innerFunIsIdent := innerCall.Fun.(*ast.Ident); innerFunIsIdent && innerFunIdent.String() == "rune" {
436
+ // Translate string(rune(val)) to String.fromCharCode(val)
437
+ if len(innerCall.Args) == 1 {
438
+ c.tsw.WriteLiterally("String.fromCharCode(")
439
+ if err := c.WriteValueExpr(innerCall.Args[0]); err != nil {
440
+ return fmt.Errorf("failed to write argument for string(rune) conversion: %w", err)
441
+ }
442
+ c.tsw.WriteLiterally(")")
443
+ return nil // Handled string(rune)
444
+ }
445
+ }
446
+ }
447
+
448
+ // Handle direct string(int32) conversion
449
+ // This assumes 'rune' is int32
450
+ if tv, ok := c.pkg.TypesInfo.Types[arg]; ok {
451
+ if basic, isBasic := tv.Type.Underlying().(*gtypes.Basic); isBasic && basic.Kind() == gtypes.Int32 {
452
+ // Translate string(rune_val) to String.fromCharCode(rune_val)
453
+ c.tsw.WriteLiterally("String.fromCharCode(")
454
+ if err := c.WriteValueExpr(arg); err != nil {
455
+ return fmt.Errorf("failed to write argument for string(int32) conversion: %w", err)
456
+ }
457
+ c.tsw.WriteLiterally(")")
458
+ return nil // Handled string(int32)
459
+ }
460
+ }
461
+ }
462
+ // Return error for other unhandled string conversions
463
+ return fmt.Errorf("unhandled string conversion: %s", exp.Fun)
464
+ case "close":
465
+ // Translate close(ch) to ch.close()
466
+ if len(exp.Args) == 1 {
467
+ if err := c.WriteValueExpr(exp.Args[0]); err != nil {
468
+ return fmt.Errorf("failed to write channel in close call: %w", err)
469
+ }
470
+ c.tsw.WriteLiterally(".close()")
471
+ return nil // Handled close
472
+ }
473
+ return errors.New("unhandled close call with incorrect number of arguments")
474
+ case "append":
475
+ // Translate append(slice, elements...) to goscript.append(slice, elements...)
476
+ if len(exp.Args) >= 1 {
477
+ c.tsw.WriteLiterally("goscript.append(")
478
+ // The first argument is the slice
479
+ if err := c.WriteValueExpr(exp.Args[0]); err != nil {
480
+ return fmt.Errorf("failed to write slice in append call: %w", err)
481
+ }
482
+ // The remaining arguments are the elements to append
483
+ for i, arg := range exp.Args[1:] {
484
+ if i > 0 || len(exp.Args) > 1 { // Add comma before elements if there are any
485
+ c.tsw.WriteLiterally(", ")
486
+ }
487
+ if err := c.WriteValueExpr(arg); err != nil {
488
+ return fmt.Errorf("failed to write argument %d in append call: %w", i+1, err)
489
+ }
490
+ }
491
+ c.tsw.WriteLiterally(")")
492
+ return nil // Handled append
493
+ }
494
+ return errors.New("unhandled append call with incorrect number of arguments")
495
+ default:
496
+ // Check if this is an async function call
497
+ if funIdent != nil && c.isAsyncFunc(funIdent.Name) {
498
+ c.tsw.WriteLiterally("await ")
499
+ }
500
+
501
+ // Not a special built-in, treat as a regular function call
502
+ if err := c.WriteValueExpr(expFun); err != nil {
503
+ return fmt.Errorf("failed to write function expression in call: %w", err)
504
+ }
505
+ c.tsw.WriteLiterally("(")
506
+ for i, arg := range exp.Args {
507
+ if i != 0 {
508
+ c.tsw.WriteLiterally(", ")
509
+ }
510
+ if err := c.WriteValueExpr(arg); err != nil {
511
+ return fmt.Errorf("failed to write argument %d in call: %w", i, err)
512
+ }
513
+ }
514
+ c.tsw.WriteLiterally(")")
515
+ return nil // Handled regular function call
516
+ }
517
+ } else {
518
+ // Not an identifier (e.g., method call on a value)
519
+ if err := c.WriteValueExpr(expFun); err != nil {
520
+ return fmt.Errorf("failed to write method expression in call: %w", err)
521
+ }
522
+ }
523
+ c.tsw.WriteLiterally("(")
524
+ for i, arg := range exp.Args {
525
+ if i != 0 {
526
+ c.tsw.WriteLiterally(", ")
527
+ }
528
+ if err := c.WriteValueExpr(arg); err != nil {
529
+ return fmt.Errorf("failed to write argument %d in call: %w", i, err)
530
+ }
531
+ }
532
+ c.tsw.WriteLiterally(")")
533
+ return nil
534
+ }
535
+
536
+ // WriteUnaryExprValue writes a unary operation on a value.
537
+ func (c *GoToTSCompiler) WriteUnaryExprValue(exp *ast.UnaryExpr) error {
538
+ if exp.Op == token.ARROW {
539
+ // Channel receive: <-ch becomes await ch.receive()
540
+ c.tsw.WriteLiterally("await ")
541
+ if err := c.WriteValueExpr(exp.X); err != nil {
542
+ return fmt.Errorf("failed to write channel receive operand: %w", err)
543
+ }
544
+ c.tsw.WriteLiterally(".receive()")
545
+ return nil
546
+ }
547
+
548
+ if exp.Op == token.AND {
549
+ // Address-of operator (&) might translate to just the value in TS,
550
+ // or potentially involve reference objects if complex pointer logic is needed.
551
+ // For now, just write the operand.
552
+ if err := c.WriteValueExpr(exp.X); err != nil {
553
+ return fmt.Errorf("failed to write unary expression operand for address-of: %w", err)
554
+ }
555
+ return nil
556
+ }
557
+
558
+ tokStr, ok := gstypes.TokenToTs(exp.Op)
559
+ if !ok {
560
+ c.tsw.WriteCommentInline(fmt.Sprintf("unhandled unary op: %s", exp.Op.String()))
561
+ } else {
562
+ c.tsw.WriteLiterally(tokStr)
563
+ }
564
+ if err := c.WriteValueExpr(exp.X); err != nil {
565
+ return fmt.Errorf("failed to write unary expression operand: %w", err)
566
+ }
567
+ return nil
568
+ }
569
+
570
+ // WriteBinaryExprValue writes a binary operation on values.
571
+ func (c *GoToTSCompiler) WriteBinaryExprValue(exp *ast.BinaryExpr) error {
572
+ // Handle special cases like channel send
573
+ if exp.Op == token.ARROW {
574
+ // Channel send: ch <- val becomes await ch.send(val)
575
+ c.tsw.WriteLiterally("await ")
576
+ if err := c.WriteValueExpr(exp.X); err != nil {
577
+ return fmt.Errorf("failed to write channel send target: %w", err)
578
+ }
579
+ c.tsw.WriteLiterally(".send(")
580
+ if err := c.WriteValueExpr(exp.Y); err != nil {
581
+ return fmt.Errorf("failed to write channel send value: %w", err)
582
+ }
583
+ c.tsw.WriteLiterally(")")
584
+ return nil
585
+ }
586
+
587
+ if err := c.WriteValueExpr(exp.X); err != nil {
588
+ return fmt.Errorf("failed to write binary expression left operand: %w", err)
589
+ }
590
+ c.tsw.WriteLiterally(" ")
591
+ tokStr, ok := gstypes.TokenToTs(exp.Op)
592
+ if !ok {
593
+ c.tsw.WriteCommentInline(fmt.Sprintf("unhandled binary op: %s", exp.Op.String()))
594
+ c.tsw.WriteLiterally(" /* op */ ")
595
+ } else {
596
+ c.tsw.WriteLiterally(tokStr)
597
+ }
598
+ c.tsw.WriteLiterally(" ")
599
+ if err := c.WriteValueExpr(exp.Y); err != nil {
600
+ return fmt.Errorf("failed to write binary expression right operand: %w", err)
601
+ }
602
+ return nil
603
+ }
604
+
605
+ // WriteBasicLitValue writes a basic literal value.
606
+ func (c *GoToTSCompiler) WriteBasicLitValue(exp *ast.BasicLit) {
607
+ if exp.Kind == token.CHAR {
608
+ // Go char literal 'x' is a rune (int32). Translate to its numeric code point.
609
+ // Use strconv.UnquoteChar to handle escape sequences correctly.
610
+ val, _, _, err := strconv.UnquoteChar(exp.Value[1:len(exp.Value)-1], '\'')
611
+ if err != nil {
612
+ c.tsw.WriteCommentInline(fmt.Sprintf("error parsing char literal %s: %v", exp.Value, err))
613
+ c.tsw.WriteLiterally("0") // Default to 0 on error
614
+ } else {
615
+ c.tsw.WriteLiterally(fmt.Sprintf("%d", val))
616
+ }
617
+ } else {
618
+ // Other literals (INT, FLOAT, STRING, IMAG)
619
+ c.tsw.WriteLiterally(exp.Value)
620
+ }
621
+ }
622
+
623
+ /*
624
+ WriteCompositeLitValue writes a composite literal value.
625
+ For array literals, uses type information to determine array length and element type,
626
+ and fills uninitialized elements with the correct zero value.
627
+ For map literals, creates a new Map with entries.
628
+ */
629
+ func (c *GoToTSCompiler) WriteCompositeLitValue(exp *ast.CompositeLit) error {
630
+ if exp.Type != nil {
631
+ // Handle map literals: map[K]V{k1: v1, k2: v2}
632
+ if _, isMapType := exp.Type.(*ast.MapType); isMapType {
633
+ c.tsw.WriteLiterally("new Map([")
634
+
635
+ // Add each key-value pair as an entry
636
+ for i, elm := range exp.Elts {
637
+ if i > 0 {
638
+ c.tsw.WriteLiterally(", ")
639
+ }
640
+
641
+ if kv, ok := elm.(*ast.KeyValueExpr); ok {
642
+ c.tsw.WriteLiterally("[")
643
+ if err := c.WriteValueExpr(kv.Key); err != nil {
644
+ return fmt.Errorf("failed to write map literal key: %w", err)
645
+ }
646
+ c.tsw.WriteLiterally(", ")
647
+ if err := c.WriteValueExpr(kv.Value); err != nil {
648
+ return fmt.Errorf("failed to write map literal value: %w", err)
649
+ }
650
+ c.tsw.WriteLiterally("]")
651
+ } else {
652
+ return fmt.Errorf("map literal elements must be key-value pairs")
653
+ }
654
+ }
655
+
656
+ c.tsw.WriteLiterally("])")
657
+ return nil
658
+ }
659
+
660
+ // Handle array literals
661
+ if arrType, isArrayType := exp.Type.(*ast.ArrayType); isArrayType {
662
+ c.tsw.WriteLiterally("[")
663
+ // Use type info to get array length and element type
664
+ var arrayLen int
665
+ var elemType ast.Expr
666
+ var goElemType interface{}
667
+ if typ := c.pkg.TypesInfo.TypeOf(exp.Type); typ != nil {
668
+ if at, ok := typ.Underlying().(*gtypes.Array); ok {
669
+ arrayLen = int(at.Len())
670
+ goElemType = at.Elem()
671
+ }
672
+ }
673
+ if arrType.Len != nil {
674
+ // Try to evaluate the length from the AST if not available from type info
675
+ if bl, ok := arrType.Len.(*ast.BasicLit); ok && bl.Kind == token.INT {
676
+ if _, err := fmt.Sscan(bl.Value, &arrayLen); err != nil {
677
+ return fmt.Errorf("failed to parse array length from basic literal: %w", err)
678
+ }
679
+ }
680
+ }
681
+ elemType = arrType.Elt
682
+
683
+ // Map of index -> value
684
+ elements := make(map[int]ast.Expr)
685
+ orderedCount := 0
686
+ maxIndex := -1
687
+ hasKeyedElements := false
688
+
689
+ for _, elm := range exp.Elts {
690
+ if kv, ok := elm.(*ast.KeyValueExpr); ok {
691
+ if lit, ok := kv.Key.(*ast.BasicLit); ok && lit.Kind == token.INT {
692
+ var index int
693
+ if _, err := fmt.Sscan(lit.Value, &index); err != nil {
694
+ return fmt.Errorf("failed to parse array index from basic literal: %w", err)
695
+ }
696
+ elements[index] = kv.Value
697
+ if index > maxIndex {
698
+ maxIndex = index
699
+ }
700
+ hasKeyedElements = true
701
+ } else {
702
+ c.tsw.WriteCommentInline("unhandled keyed array literal key type")
703
+ if err := c.WriteValueExpr(elm); err != nil {
704
+ return fmt.Errorf("failed to write keyed array literal element with unhandled key type: %w", err)
705
+ }
706
+ }
707
+ } else {
708
+ elements[orderedCount] = elm
709
+ if orderedCount > maxIndex {
710
+ maxIndex = orderedCount
711
+ }
712
+ orderedCount++
713
+ }
714
+ }
715
+
716
+ // Determine array length
717
+ if arrayLen == 0 {
718
+ // If length is not set, infer from max index or number of elements
719
+ if hasKeyedElements {
720
+ arrayLen = maxIndex + 1
721
+ } else {
722
+ arrayLen = len(exp.Elts)
723
+ }
724
+ }
725
+
726
+ for i := 0; i < arrayLen; i++ {
727
+ if i > 0 {
728
+ c.tsw.WriteLiterally(", ")
729
+ }
730
+ if elm, ok := elements[i]; ok && elm != nil {
731
+ if err := c.WriteValueExpr(elm); err != nil {
732
+ return fmt.Errorf("failed to write array literal element: %w", err)
733
+ }
734
+ } else {
735
+ // Write zero value for element type
736
+ if goElemType != nil {
737
+ c.WriteZeroValueForType(goElemType)
738
+ } else {
739
+ c.WriteZeroValueForType(elemType)
740
+ }
741
+ }
742
+ }
743
+ c.tsw.WriteLiterally("]")
744
+ return nil
745
+ } else {
746
+ // Typed literal, likely a struct: new Type({...})
747
+ c.tsw.WriteLiterally("new ")
748
+ c.WriteTypeExpr(exp.Type)
749
+ c.tsw.WriteLiterally("({ ")
750
+ for i, elm := range exp.Elts {
751
+ if i != 0 {
752
+ c.tsw.WriteLiterally(", ")
753
+ }
754
+ if err := c.WriteValueExpr(elm); err != nil {
755
+ return fmt.Errorf("failed to write struct literal field: %w", err)
756
+ }
757
+ }
758
+ c.tsw.WriteLiterally(" })")
759
+ return nil
760
+ }
761
+ }
762
+
763
+ // Untyped composite literal. Could be array, slice, map.
764
+ // Requires type information for accurate translation.
765
+ // Defaulting to an object literal {} as a slightly safer guess than array []
766
+ // if it contains KeyValueExpr, otherwise default to array.
767
+ isLikelyObject := false
768
+ if len(exp.Elts) > 0 {
769
+ if _, ok := exp.Elts[0].(*ast.KeyValueExpr); ok {
770
+ isLikelyObject = true
771
+ }
772
+ }
773
+
774
+ if isLikelyObject {
775
+ c.tsw.WriteLiterally("{ ")
776
+ } else {
777
+ c.tsw.WriteLiterally("[ ")
778
+ }
779
+
780
+ for i, elm := range exp.Elts {
781
+ if i != 0 {
782
+ c.tsw.WriteLiterally(", ")
783
+ }
784
+ if err := c.WriteValueExpr(elm); err != nil {
785
+ return fmt.Errorf("failed to write untyped composite literal element: %w", err)
786
+ }
787
+ }
788
+ if isLikelyObject {
789
+ c.tsw.WriteLiterally(" }")
790
+ } else {
791
+ c.tsw.WriteLiterally(" ]")
792
+ }
793
+ // c.tsw.WriteCommentInline("untyped composite literal, type guessed")
794
+ return nil
795
+ }
796
+
797
+ // WriteKeyValueExprValue writes a key-value pair.
798
+ // Returns an error if writing the key or value fails.
799
+ func (c *GoToTSCompiler) WriteKeyValueExprValue(exp *ast.KeyValueExpr) error {
800
+ // Keep original Go casing for keys
801
+ if err := c.WriteValueExpr(exp.Key); err != nil {
802
+ return fmt.Errorf("failed to write key-value expression key: %w", err)
803
+ }
804
+ c.tsw.WriteLiterally(": ")
805
+ if err := c.WriteValueExpr(exp.Value); err != nil {
806
+ return fmt.Errorf("failed to write key-value expression value: %w", err)
807
+ }
808
+ return nil
809
+ }
810
+
811
+ // WriteFuncLitValue writes a function literal as a TypeScript arrow function.
812
+ func (c *GoToTSCompiler) WriteFuncLitValue(exp *ast.FuncLit) error {
813
+ // Determine if the function literal should be async
814
+ isAsync := c.containsAsyncOperations(exp.Body)
815
+
816
+ if isAsync {
817
+ c.tsw.WriteLiterally("async ")
818
+ }
819
+
820
+ // Write arrow function: (params) => { body }
821
+ c.tsw.WriteLiterally("(")
822
+ c.WriteFieldList(exp.Type.Params, true) // true = arguments
823
+ c.tsw.WriteLiterally(") => ")
824
+
825
+ // Write function body
826
+ if err := c.WriteStmt(exp.Body); err != nil {
827
+ return fmt.Errorf("failed to write function literal body: %w", err)
828
+ }
829
+
830
+ return nil
831
+ }