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.
- package/.aider-prompt +11 -0
- package/LICENSE +21 -0
- package/README.md +427 -0
- package/builtin/builtin.ts +507 -0
- package/cmd/goscript/cmd_compile.go +59 -0
- package/cmd/goscript/main.go +23 -0
- package/compiler/compile.go +183 -0
- package/compiler/compile_comment.go +41 -0
- package/compiler/compile_decls.go +72 -0
- package/compiler/compile_expr.go +831 -0
- package/compiler/compile_field.go +89 -0
- package/compiler/compile_spec.go +256 -0
- package/compiler/compile_stmt.go +1509 -0
- package/compiler/compiler.go +81 -0
- package/compiler/config.go +32 -0
- package/compiler/context.go +9 -0
- package/compiler/file_compiler.go +80 -0
- package/compiler/output_path.go +31 -0
- package/compiler/pkg_compiler.go +73 -0
- package/compiler/writer.go +90 -0
- package/compliance/COMPLIANCE.md +133 -0
- package/compliance/compliance.go +313 -0
- package/compliance/compliance_test.go +57 -0
- package/compliance/tests/array_literal/array_literal.go +15 -0
- package/compliance/tests/array_literal/array_literal.gs.ts +19 -0
- package/compliance/tests/array_literal/expected.log +3 -0
- package/compliance/tests/async_basic/async_basic.go +26 -0
- package/compliance/tests/async_basic/async_basic.gs.ts +30 -0
- package/compliance/tests/async_basic/expected.log +1 -0
- package/compliance/tests/basic_arithmetic/basic_arithmetic.go +15 -0
- package/compliance/tests/basic_arithmetic/basic_arithmetic.gs.ts +19 -0
- package/compliance/tests/basic_arithmetic/expected.log +5 -0
- package/compliance/tests/boolean_logic/boolean_logic.go +13 -0
- package/compliance/tests/boolean_logic/boolean_logic.gs.ts +17 -0
- package/compliance/tests/boolean_logic/expected.log +3 -0
- package/compliance/tests/channel_basic/channel_basic.go +12 -0
- package/compliance/tests/channel_basic/channel_basic.gs.ts +18 -0
- package/compliance/tests/channel_basic/expected.log +1 -0
- package/compliance/tests/composite_literal_assignment/composite_literal_assignment.go +20 -0
- package/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.ts +27 -0
- package/compliance/tests/composite_literal_assignment/expected.log +2 -0
- package/compliance/tests/constants/constants.go +18 -0
- package/compliance/tests/constants/constants.gs.ts +22 -0
- package/compliance/tests/constants/expected.log +3 -0
- package/compliance/tests/copy_independence/copy_independence.go +29 -0
- package/compliance/tests/copy_independence/copy_independence.gs.ts +36 -0
- package/compliance/tests/copy_independence/expected.log +4 -0
- package/compliance/tests/float64/expected.log +6 -0
- package/compliance/tests/float64/float64.go +28 -0
- package/compliance/tests/float64/float64.gs.ts +32 -0
- package/compliance/tests/for_loop_basic/expected.log +5 -0
- package/compliance/tests/for_loop_basic/for_loop_basic.go +9 -0
- package/compliance/tests/for_loop_basic/for_loop_basic.gs.ts +13 -0
- package/compliance/tests/for_loop_condition_only/expected.log +5 -0
- package/compliance/tests/for_loop_condition_only/main.go +9 -0
- package/compliance/tests/for_loop_condition_only/main.gs.ts +13 -0
- package/compliance/tests/for_range/expected.log +9 -0
- package/compliance/tests/for_range/for_range.go +26 -0
- package/compliance/tests/for_range/for_range.gs.ts +45 -0
- package/compliance/tests/for_range_index_use/expected.log +6 -0
- package/compliance/tests/for_range_index_use/for_range_index_use.go +11 -0
- package/compliance/tests/for_range_index_use/for_range_index_use.gs.ts +18 -0
- package/compliance/tests/func_literal/expected.log +1 -0
- package/compliance/tests/func_literal/func_literal.go +10 -0
- package/compliance/tests/func_literal/func_literal.gs.ts +15 -0
- package/compliance/tests/function_call_result_assignment/expected.log +2 -0
- package/compliance/tests/function_call_result_assignment/function_call_result_assignment.go +24 -0
- package/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.ts +31 -0
- package/compliance/tests/if_statement/expected.log +1 -0
- package/compliance/tests/if_statement/if_statement.go +11 -0
- package/compliance/tests/if_statement/if_statement.gs.ts +15 -0
- package/compliance/tests/interface_to_interface_type_assertion/expected.log +1 -0
- package/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.go +30 -0
- package/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.ts +41 -0
- package/compliance/tests/interface_type_assertion/expected.log +1 -0
- package/compliance/tests/interface_type_assertion/interface_type_assertion.go +26 -0
- package/compliance/tests/interface_type_assertion/interface_type_assertion.gs.ts +36 -0
- package/compliance/tests/map_support/expected.log +13 -0
- package/compliance/tests/map_support/map_support.go +89 -0
- package/compliance/tests/map_support/map_support.gs.ts +102 -0
- package/compliance/tests/method_call_on_pointer_receiver/expected.log +1 -0
- package/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.go +19 -0
- package/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.ts +27 -0
- package/compliance/tests/method_call_on_pointer_via_value/expected.log +1 -0
- package/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.go +29 -0
- package/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.ts +38 -0
- package/compliance/tests/method_call_on_value_receiver/expected.log +1 -0
- package/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.go +16 -0
- package/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.ts +24 -0
- package/compliance/tests/method_call_on_value_via_pointer/expected.log +2 -0
- package/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.go +30 -0
- package/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.ts +38 -0
- package/compliance/tests/multiple_return_values/expected.log +6 -0
- package/compliance/tests/multiple_return_values/multiple_return_values.go +19 -0
- package/compliance/tests/multiple_return_values/multiple_return_values.gs.ts +23 -0
- package/compliance/tests/pointer_assignment_no_copy/expected.log +2 -0
- package/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.go +28 -0
- package/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.ts +35 -0
- package/compliance/tests/pointer_composite_literal_assignment/expected.log +3 -0
- package/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.go +23 -0
- package/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.ts +30 -0
- package/compliance/tests/pointer_deref_multiassign/expected.log +0 -0
- package/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.go +17 -0
- package/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.ts +27 -0
- package/compliance/tests/pointer_initialization/expected.log +1 -0
- package/compliance/tests/pointer_initialization/pointer_initialization.go +16 -0
- package/compliance/tests/pointer_initialization/pointer_initialization.gs.ts +22 -0
- package/compliance/tests/select_receive_on_closed_channel_no_default/expected.log +1 -0
- package/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.go +15 -0
- package/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.ts +31 -0
- package/compliance/tests/select_send_on_full_buffered_channel_with_default/expected.log +1 -0
- package/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.go +13 -0
- package/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.gs.ts +35 -0
- package/compliance/tests/select_statement/expected.log +9 -0
- package/compliance/tests/select_statement/select_statement.go +109 -0
- package/compliance/tests/select_statement/select_statement.gs.ts +239 -0
- package/compliance/tests/simple/expected.log +1 -0
- package/compliance/tests/simple/simple.go +5 -0
- package/compliance/tests/simple/simple.gs.ts +9 -0
- package/compliance/tests/simple_deref_assignment/expected.log +2 -0
- package/compliance/tests/simple_deref_assignment/simple_deref_assignment.go +19 -0
- package/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.ts +26 -0
- package/compliance/tests/slices/expected.log +7 -0
- package/compliance/tests/slices/slices.go +22 -0
- package/compliance/tests/slices/slices.gs.ts +26 -0
- package/compliance/tests/string_rune_conversion/expected.log +3 -0
- package/compliance/tests/string_rune_conversion/string_rune_conversion.go +16 -0
- package/compliance/tests/string_rune_conversion/string_rune_conversion.gs.ts +22 -0
- package/compliance/tests/struct_field_access/expected.log +2 -0
- package/compliance/tests/struct_field_access/struct_field_access.go +13 -0
- package/compliance/tests/struct_field_access/struct_field_access.gs.ts +20 -0
- package/compliance/tests/struct_value_init_clone/expected.log +5 -0
- package/compliance/tests/struct_value_init_clone/struct_value_init_clone.go +28 -0
- package/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.ts +35 -0
- package/compliance/tests/switch_statement/expected.log +14 -0
- package/compliance/tests/switch_statement/switch_statement.go +59 -0
- package/compliance/tests/switch_statement/switch_statement.gs.ts +85 -0
- package/compliance/tests/value_type_copy_behavior/expected.log +3 -0
- package/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.go +25 -0
- package/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.ts +34 -0
- package/design/DESIGN.md +599 -0
- package/example/simple/build.bash +10 -0
- package/example/simple/go.mod +23 -0
- package/example/simple/go.sum +39 -0
- package/example/simple/main.go +138 -0
- package/example/simple/main.gs.ts +133 -0
- package/example/simple/main.ts +3 -0
- package/example/simple/main_test.go +59 -0
- package/example/simple/main_tools.go +5 -0
- package/example/simple/package.json +7 -0
- package/example/simple/run.bash +6 -0
- package/example/simple/tsconfig.json +28 -0
- package/example/simple/yarn.lock +8 -0
- package/go.mod +22 -0
- package/go.sum +39 -0
- package/output/output.go +10 -0
- package/package.json +14 -0
- package/tsconfig.json +10 -0
- package/types/tokens.go +65 -0
- 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
|
+
}
|