goscript 0.0.22 → 0.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cmd/goscript/cmd_compile.go +2 -2
- package/compiler/analysis.go +229 -51
- package/compiler/assignment.go +6 -1
- package/compiler/compiler.go +41 -18
- package/compiler/compiler_test.go +36 -8
- package/compiler/composite-lit.go +25 -10
- package/compiler/decl.go +36 -0
- package/compiler/expr-call.go +116 -60
- package/compiler/expr-selector.go +22 -2
- package/compiler/expr-type.go +131 -4
- package/compiler/expr-value.go +7 -37
- package/compiler/expr.go +247 -12
- package/compiler/field.go +3 -3
- package/compiler/lit.go +34 -2
- package/compiler/primitive.go +8 -2
- package/compiler/spec-struct.go +137 -6
- package/compiler/spec-value.go +50 -18
- package/compiler/spec.go +12 -3
- package/compiler/stmt-assign.go +29 -1
- package/compiler/stmt-range.go +9 -11
- package/compiler/stmt-select.go +211 -0
- package/compiler/stmt-type-switch.go +147 -0
- package/compiler/stmt.go +129 -244
- package/compiler/type-assert.go +125 -379
- package/compiler/type.go +187 -125
- package/package.json +5 -5
- package/builtin/builtin.go +0 -11
- package/builtin/builtin.ts +0 -2379
- package/dist/builtin/builtin.d.ts +0 -513
- package/dist/builtin/builtin.js +0 -1686
- package/dist/builtin/builtin.js.map +0 -1
|
@@ -15,7 +15,8 @@ import (
|
|
|
15
15
|
// - Map literals (e.g., `map[K]V{k1: v1}`): Translated to `new Map([[k1_ts, v1_ts]])`.
|
|
16
16
|
// Values are processed by `writeBoxedValue`.
|
|
17
17
|
// - Array/Slice literals (e.g., `[]T{e1, e2}`, `[N]T{idx: val}`):
|
|
18
|
-
//
|
|
18
|
+
// - For `[]byte{...}`, translated to `new Uint8Array([...])`.
|
|
19
|
+
// - For other `[]T` or `[N]T`, translated using the `$.arrayToSlice<T_ts>([...])` runtime helper.
|
|
19
20
|
// It handles both keyed and unkeyed elements, infers length if necessary,
|
|
20
21
|
// and uses zero values for uninitialized array elements.
|
|
21
22
|
// Multi-dimensional arrays/slices pass a depth parameter to `$.arrayToSlice`.
|
|
@@ -73,14 +74,29 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
73
74
|
// We'll handle this with depth parameter to arrayToSlice
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
|
|
77
|
+
// Check if it's a []byte literal
|
|
78
|
+
isByteSliceLiteral := false
|
|
79
|
+
if typInfo := c.pkg.TypesInfo.TypeOf(exp.Type); typInfo != nil {
|
|
80
|
+
if sliceT, ok := typInfo.Underlying().(*types.Slice); ok {
|
|
81
|
+
if basicElem, ok := sliceT.Elem().(*types.Basic); ok && basicElem.Kind() == types.Uint8 {
|
|
82
|
+
isByteSliceLiteral = true
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
77
86
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
87
|
+
if isByteSliceLiteral {
|
|
88
|
+
c.tsw.WriteLiterally("new Uint8Array")
|
|
89
|
+
} else {
|
|
90
|
+
c.tsw.WriteLiterally("$.arrayToSlice")
|
|
91
|
+
|
|
92
|
+
// write the type annotation
|
|
93
|
+
c.tsw.WriteLiterally("<")
|
|
94
|
+
// Write the element type using the existing function
|
|
95
|
+
c.WriteTypeExpr(arrType.Elt)
|
|
96
|
+
c.tsw.WriteLiterally(">")
|
|
97
|
+
}
|
|
83
98
|
|
|
99
|
+
// opening
|
|
84
100
|
c.tsw.WriteLiterally("([")
|
|
85
101
|
|
|
86
102
|
// Use type info to get array length and element type
|
|
@@ -166,7 +182,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
166
182
|
c.tsw.WriteLiterally("]")
|
|
167
183
|
|
|
168
184
|
// If it's a multi-dimensional array/slice, use depth=2 to convert nested arrays
|
|
169
|
-
if isMultiDimensional {
|
|
185
|
+
if isMultiDimensional && !isByteSliceLiteral { // Depth parameter not applicable to Uint8Array constructor
|
|
170
186
|
c.tsw.WriteLiterally(", 2") // Depth of 2 for one level of nesting
|
|
171
187
|
}
|
|
172
188
|
|
|
@@ -271,9 +287,8 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
271
287
|
}
|
|
272
288
|
|
|
273
289
|
// Handle the case where an anonymous struct has values without keys
|
|
274
|
-
// Handle the case where an anonymous struct has values without keys.
|
|
275
290
|
// This block processes non-key-value elements and associates them with struct fields.
|
|
276
|
-
if isAnonymousStruct && len(exp.Elts) > 0 && len(directFields) == 0
|
|
291
|
+
if isAnonymousStruct && len(exp.Elts) > 0 && len(directFields) == 0 {
|
|
277
292
|
// Check if any elements in the composite literal are not key-value pairs.
|
|
278
293
|
hasNonKeyValueElts := false
|
|
279
294
|
for _, elt := range exp.Elts {
|
package/compiler/decl.go
CHANGED
|
@@ -88,14 +88,50 @@ func (c *GoToTSCompiler) WriteFuncDeclAsFunction(decl *ast.FuncDecl) error {
|
|
|
88
88
|
return fmt.Errorf("failed to write function name: %w", err)
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
// Write type parameters if present
|
|
92
|
+
if decl.Type.TypeParams != nil {
|
|
93
|
+
c.WriteTypeParameters(decl.Type.TypeParams)
|
|
94
|
+
}
|
|
95
|
+
|
|
91
96
|
// WriteFuncType needs to be aware if the function is async
|
|
92
97
|
c.WriteFuncType(decl.Type, isAsync) // Write signature (params, return type)
|
|
93
98
|
c.tsw.WriteLiterally(" ")
|
|
94
99
|
|
|
100
|
+
hasNamedReturns := false
|
|
101
|
+
if decl.Type.Results != nil {
|
|
102
|
+
for _, field := range decl.Type.Results.List {
|
|
103
|
+
if len(field.Names) > 0 {
|
|
104
|
+
hasNamedReturns = true
|
|
105
|
+
break
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if hasNamedReturns {
|
|
111
|
+
c.tsw.WriteLine("{")
|
|
112
|
+
c.tsw.Indent(1)
|
|
113
|
+
|
|
114
|
+
// Declare named return variables and initialize them to their zero values
|
|
115
|
+
for _, field := range decl.Type.Results.List {
|
|
116
|
+
for _, name := range field.Names {
|
|
117
|
+
c.tsw.WriteLiterallyf("let %s: ", name.Name)
|
|
118
|
+
c.WriteTypeExpr(field.Type)
|
|
119
|
+
c.tsw.WriteLiterally(" = ")
|
|
120
|
+
c.WriteZeroValueForType(c.pkg.TypesInfo.TypeOf(field.Type))
|
|
121
|
+
c.tsw.WriteLine("")
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
95
126
|
if err := c.WriteStmt(decl.Body); err != nil {
|
|
96
127
|
return fmt.Errorf("failed to write function body: %w", err)
|
|
97
128
|
}
|
|
98
129
|
|
|
130
|
+
if hasNamedReturns {
|
|
131
|
+
c.tsw.Indent(-1)
|
|
132
|
+
c.tsw.WriteLine("}")
|
|
133
|
+
}
|
|
134
|
+
|
|
99
135
|
return nil
|
|
100
136
|
}
|
|
101
137
|
|
package/compiler/expr-call.go
CHANGED
|
@@ -13,15 +13,19 @@ import (
|
|
|
13
13
|
// into its TypeScript equivalent.
|
|
14
14
|
// It handles several Go built-in functions specially:
|
|
15
15
|
// - `println(...)` becomes `console.log(...)`.
|
|
16
|
+
// - `panic(...)` becomes `$.panic(...)`.
|
|
16
17
|
// - `len(arg)` becomes `$.len(arg)`.
|
|
17
18
|
// - `cap(arg)` becomes `$.cap(arg)`.
|
|
18
19
|
// - `delete(m, k)` becomes `$.deleteMapEntry(m, k)`.
|
|
19
20
|
// - `make(chan T, size)` becomes `$.makeChannel<T_ts>(size, zeroValueForT)`.
|
|
20
21
|
// - `make(map[K]V)` becomes `$.makeMap<K_ts, V_ts>()`.
|
|
21
22
|
// - `make([]T, len, cap)` becomes `$.makeSlice<T_ts>(len, cap)`.
|
|
23
|
+
// - `make([]byte, len, cap)` becomes `new Uint8Array(len)`.
|
|
22
24
|
// - `string(runeVal)` becomes `String.fromCharCode(runeVal)`.
|
|
23
|
-
// - `string([]runeVal)`
|
|
24
|
-
// - `[]
|
|
25
|
+
// - `string([]runeVal)` becomes `$.runesToString(sliceVal)`.
|
|
26
|
+
// - `string([]byteVal)` becomes `$.bytesToString(sliceVal)`.
|
|
27
|
+
// - `[]rune(stringVal)` becomes `$.stringToRunes(stringVal)“.
|
|
28
|
+
// - `[]byte(stringVal)` becomes `$.stringToBytes(stringVal)`.
|
|
25
29
|
// - `close(ch)` becomes `ch.close()`.
|
|
26
30
|
// - `append(slice, elems...)` becomes `$.append(slice, elems...)`.
|
|
27
31
|
// - `byte(val)` becomes `$.byte(val)`.
|
|
@@ -70,66 +74,56 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
70
74
|
}
|
|
71
75
|
}
|
|
72
76
|
}
|
|
77
|
+
// Check if it's a []byte type and the argument is a string
|
|
78
|
+
if eltIdent, ok := arrayType.Elt.(*ast.Ident); ok && eltIdent.Name == "byte" && arrayType.Len == nil {
|
|
79
|
+
if len(exp.Args) == 1 {
|
|
80
|
+
arg := exp.Args[0]
|
|
81
|
+
// Ensure TypesInfo is available and the argument type can be determined
|
|
82
|
+
if tv, typeOk := c.pkg.TypesInfo.Types[arg]; typeOk && tv.Type != nil {
|
|
83
|
+
if basicArgType, isBasic := tv.Type.Underlying().(*types.Basic); isBasic && (basicArgType.Info()&types.IsString) != 0 {
|
|
84
|
+
c.tsw.WriteLiterally("$.stringToBytes(")
|
|
85
|
+
if err := c.WriteValueExpr(arg); err != nil {
|
|
86
|
+
return fmt.Errorf("failed to write argument for []byte(string) conversion: %w", err)
|
|
87
|
+
}
|
|
88
|
+
c.tsw.WriteLiterally(")")
|
|
89
|
+
return nil // Handled []byte(string)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
73
94
|
}
|
|
74
95
|
|
|
75
96
|
if funIdent, funIsIdent := expFun.(*ast.Ident); funIsIdent {
|
|
76
97
|
switch funIdent.String() {
|
|
98
|
+
case "panic":
|
|
99
|
+
c.tsw.WriteLiterally("$.panic")
|
|
77
100
|
case "println":
|
|
78
|
-
c.tsw.WriteLiterally("console.log
|
|
79
|
-
for i, arg := range exp.Args {
|
|
80
|
-
if i != 0 {
|
|
81
|
-
c.tsw.WriteLiterally(", ")
|
|
82
|
-
}
|
|
83
|
-
if err := c.WriteValueExpr(arg); err != nil {
|
|
84
|
-
return err
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
c.tsw.WriteLiterally(")")
|
|
88
|
-
return nil
|
|
101
|
+
c.tsw.WriteLiterally("console.log")
|
|
89
102
|
case "len":
|
|
90
103
|
// Translate len(arg) to $.len(arg)
|
|
91
|
-
if len(exp.Args)
|
|
92
|
-
|
|
93
|
-
if err := c.WriteValueExpr(exp.Args[0]); err != nil {
|
|
94
|
-
return err
|
|
95
|
-
}
|
|
96
|
-
c.tsw.WriteLiterally(")")
|
|
97
|
-
return nil // Handled len
|
|
104
|
+
if len(exp.Args) != 1 {
|
|
105
|
+
return errors.Errorf("unhandled len call with incorrect number of arguments: %d != 1", len(exp.Args))
|
|
98
106
|
}
|
|
99
|
-
|
|
107
|
+
c.tsw.WriteLiterally("$.len")
|
|
100
108
|
case "cap":
|
|
101
109
|
// Translate cap(arg) to $.cap(arg)
|
|
102
|
-
if len(exp.Args)
|
|
103
|
-
|
|
104
|
-
if err := c.WriteValueExpr(exp.Args[0]); err != nil {
|
|
105
|
-
return err
|
|
106
|
-
}
|
|
107
|
-
c.tsw.WriteLiterally(")")
|
|
108
|
-
return nil // Handled cap
|
|
110
|
+
if len(exp.Args) != 1 {
|
|
111
|
+
return errors.Errorf("unhandled cap call with incorrect number of arguments: %d != 1", len(exp.Args))
|
|
109
112
|
}
|
|
110
|
-
|
|
113
|
+
c.tsw.WriteLiterally("$.cap")
|
|
111
114
|
case "delete":
|
|
112
115
|
// Translate delete(map, key) to $.deleteMapEntry(map, key)
|
|
113
|
-
if len(exp.Args)
|
|
114
|
-
|
|
115
|
-
if err := c.WriteValueExpr(exp.Args[0]); err != nil { // Map
|
|
116
|
-
return err
|
|
117
|
-
}
|
|
118
|
-
c.tsw.WriteLiterally(", ")
|
|
119
|
-
if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Key
|
|
120
|
-
return err
|
|
121
|
-
}
|
|
122
|
-
c.tsw.WriteLiterally(")")
|
|
123
|
-
return nil // Handled delete
|
|
116
|
+
if len(exp.Args) != 2 {
|
|
117
|
+
return errors.Errorf("unhandled delete call with incorrect number of arguments: %d != 2", len(exp.Args))
|
|
124
118
|
}
|
|
125
|
-
|
|
119
|
+
c.tsw.WriteLiterally("$.deleteMapEntry")
|
|
126
120
|
case "make":
|
|
127
121
|
// First check if we have a channel type
|
|
128
122
|
if typ := c.pkg.TypesInfo.TypeOf(exp.Args[0]); typ != nil {
|
|
129
123
|
if chanType, ok := typ.Underlying().(*types.Chan); ok {
|
|
130
124
|
// Handle channel creation: make(chan T, bufferSize) or make(chan T)
|
|
131
125
|
c.tsw.WriteLiterally("$.makeChannel<")
|
|
132
|
-
c.WriteGoType(chanType.Elem())
|
|
126
|
+
c.WriteGoType(chanType.Elem(), GoTypeContextGeneral)
|
|
133
127
|
c.tsw.WriteLiterally(">(")
|
|
134
128
|
|
|
135
129
|
// If buffer size is provided, add it
|
|
@@ -189,13 +183,30 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
189
183
|
if sliceType == nil {
|
|
190
184
|
return errors.New("could not get type information for slice in make call")
|
|
191
185
|
}
|
|
192
|
-
|
|
186
|
+
goUnderlyingType, ok := sliceType.Underlying().(*types.Slice)
|
|
193
187
|
if !ok {
|
|
194
188
|
return errors.New("expected slice type for make call")
|
|
195
189
|
}
|
|
190
|
+
goElemType := goUnderlyingType.Elem()
|
|
191
|
+
|
|
192
|
+
// Check if it's make([]byte, ...)
|
|
193
|
+
if basicElem, isBasic := goElemType.(*types.Basic); isBasic && basicElem.Kind() == types.Uint8 {
|
|
194
|
+
c.tsw.WriteLiterally("new Uint8Array(")
|
|
195
|
+
if len(exp.Args) >= 2 {
|
|
196
|
+
if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
|
|
197
|
+
return err
|
|
198
|
+
}
|
|
199
|
+
// Capacity argument for make([]byte, len, cap) is ignored for new Uint8Array(len)
|
|
200
|
+
} else {
|
|
201
|
+
// If no length is provided, default to 0
|
|
202
|
+
c.tsw.WriteLiterally("0")
|
|
203
|
+
}
|
|
204
|
+
c.tsw.WriteLiterally(")")
|
|
205
|
+
return nil // Handled make for []byte
|
|
206
|
+
}
|
|
196
207
|
|
|
197
208
|
c.tsw.WriteLiterally("$.makeSlice<")
|
|
198
|
-
c.WriteGoType(goElemType
|
|
209
|
+
c.WriteGoType(goElemType, GoTypeContextGeneral) // Write the element type
|
|
199
210
|
c.tsw.WriteLiterally(">(")
|
|
200
211
|
|
|
201
212
|
if len(exp.Args) >= 2 {
|
|
@@ -253,7 +264,16 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
253
264
|
// Handle direct string(int32) conversion
|
|
254
265
|
// This assumes 'rune' is int32
|
|
255
266
|
if tv, ok := c.pkg.TypesInfo.Types[arg]; ok {
|
|
256
|
-
|
|
267
|
+
// Case 3a: Argument is already a string - no-op
|
|
268
|
+
if basic, isBasic := tv.Type.Underlying().(*types.Basic); isBasic && basic.Kind() == types.String {
|
|
269
|
+
// Translate string(stringValue) to stringValue (no-op)
|
|
270
|
+
if err := c.WriteValueExpr(arg); err != nil {
|
|
271
|
+
return fmt.Errorf("failed to write argument for string(string) no-op conversion: %w", err)
|
|
272
|
+
}
|
|
273
|
+
return nil // Handled string(string) no-op
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if basic, isBasic := tv.Type.Underlying().(*types.Basic); isBasic && (basic.Kind() == types.Int32 || basic.Kind() == types.UntypedRune) {
|
|
257
277
|
// Translate string(rune_val) to String.fromCharCode(rune_val)
|
|
258
278
|
c.tsw.WriteLiterally("String.fromCharCode(")
|
|
259
279
|
if err := c.WriteValueExpr(arg); err != nil {
|
|
@@ -266,18 +286,43 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
266
286
|
// Case 3: Argument is a slice of runes or bytes string([]rune{...}) or string([]byte{...})
|
|
267
287
|
if sliceType, isSlice := tv.Type.Underlying().(*types.Slice); isSlice {
|
|
268
288
|
if basic, isBasic := sliceType.Elem().Underlying().(*types.Basic); isBasic {
|
|
269
|
-
// Handle
|
|
270
|
-
if basic.Kind() == types.
|
|
271
|
-
|
|
289
|
+
// Handle string([]byte)
|
|
290
|
+
if basic.Kind() == types.Uint8 {
|
|
291
|
+
c.tsw.WriteLiterally("$.bytesToString(")
|
|
292
|
+
if err := c.WriteValueExpr(arg); err != nil {
|
|
293
|
+
return fmt.Errorf("failed to write argument for string([]byte) conversion: %w", err)
|
|
294
|
+
}
|
|
295
|
+
c.tsw.WriteLiterally(")")
|
|
296
|
+
return nil // Handled string([]byte)
|
|
297
|
+
}
|
|
298
|
+
// Handle both runes (int32)
|
|
299
|
+
if basic.Kind() == types.Int32 {
|
|
300
|
+
// Translate string([]rune) to $.runesToString(...)
|
|
272
301
|
c.tsw.WriteLiterally("$.runesToString(")
|
|
273
302
|
if err := c.WriteValueExpr(arg); err != nil {
|
|
274
|
-
return fmt.Errorf("failed to write argument for string([]rune
|
|
303
|
+
return fmt.Errorf("failed to write argument for string([]rune) conversion: %w", err)
|
|
275
304
|
}
|
|
276
305
|
c.tsw.WriteLiterally(")")
|
|
277
|
-
return nil // Handled string([]rune)
|
|
306
|
+
return nil // Handled string([]rune)
|
|
278
307
|
}
|
|
279
308
|
}
|
|
280
309
|
}
|
|
310
|
+
|
|
311
|
+
// Case 4: Argument is a generic type parameter (e.g., string | []byte)
|
|
312
|
+
if typeParam, isTypeParam := tv.Type.(*types.TypeParam); isTypeParam {
|
|
313
|
+
// Check if this is a []byte | string union constraint
|
|
314
|
+
constraint := typeParam.Constraint()
|
|
315
|
+
if constraint != nil {
|
|
316
|
+
// For now, assume any type parameter that could be string or []byte needs the helper
|
|
317
|
+
// This is a heuristic - in the future we could parse the constraint more precisely
|
|
318
|
+
c.tsw.WriteLiterally("$.genericBytesOrStringToString(")
|
|
319
|
+
if err := c.WriteValueExpr(arg); err != nil {
|
|
320
|
+
return fmt.Errorf("failed to write argument for string(generic) conversion: %w", err)
|
|
321
|
+
}
|
|
322
|
+
c.tsw.WriteLiterally(")")
|
|
323
|
+
return nil // Handled string(generic type parameter)
|
|
324
|
+
}
|
|
325
|
+
}
|
|
281
326
|
}
|
|
282
327
|
}
|
|
283
328
|
// Return error for other unhandled string conversions
|
|
@@ -352,8 +397,10 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
352
397
|
return fmt.Errorf("failed to write argument for type cast: %w", err)
|
|
353
398
|
}
|
|
354
399
|
|
|
355
|
-
// Then use the TypeScript "as" operator with the type name
|
|
356
|
-
c.tsw.
|
|
400
|
+
// Then use the TypeScript "as" operator with the mapped type name
|
|
401
|
+
c.tsw.WriteLiterally(" as ")
|
|
402
|
+
c.WriteGoType(typeName.Type(), GoTypeContextGeneral)
|
|
403
|
+
c.tsw.WriteLiterally(")")
|
|
357
404
|
return nil // Handled non-function type cast
|
|
358
405
|
}
|
|
359
406
|
}
|
|
@@ -396,15 +443,24 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
396
443
|
return nil // Handled regular function call
|
|
397
444
|
}
|
|
398
445
|
} else {
|
|
399
|
-
//
|
|
400
|
-
if
|
|
401
|
-
|
|
402
|
-
|
|
446
|
+
// If expFun is a function literal, it needs to be wrapped in parentheses for IIFE syntax
|
|
447
|
+
if _, isFuncLit := expFun.(*ast.FuncLit); isFuncLit {
|
|
448
|
+
c.tsw.WriteLiterally("(")
|
|
449
|
+
if err := c.WriteValueExpr(expFun); err != nil {
|
|
450
|
+
return fmt.Errorf("failed to write function literal in call: %w", err)
|
|
451
|
+
}
|
|
452
|
+
c.tsw.WriteLiterally(")")
|
|
453
|
+
} else {
|
|
454
|
+
// Not an identifier (e.g., method call on a value)
|
|
455
|
+
if err := c.WriteValueExpr(expFun); err != nil {
|
|
456
|
+
return fmt.Errorf("failed to write method expression in call: %w", err)
|
|
457
|
+
}
|
|
403
458
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
459
|
+
if funType := c.pkg.TypesInfo.TypeOf(expFun); funType != nil {
|
|
460
|
+
if _, ok := funType.Underlying().(*types.Signature); ok {
|
|
461
|
+
if _, isNamed := funType.(*types.Named); isNamed {
|
|
462
|
+
c.tsw.WriteLiterally("!")
|
|
463
|
+
}
|
|
408
464
|
}
|
|
409
465
|
}
|
|
410
466
|
}
|
|
@@ -91,8 +91,28 @@ func (c *GoToTSCompiler) WriteSelectorExpr(exp *ast.SelectorExpr) error {
|
|
|
91
91
|
return fmt.Errorf("failed to write selector base expression: %w", err)
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
// Add
|
|
95
|
-
|
|
94
|
+
// Add null assertion for selector expressions when accessing fields/methods on nullable types
|
|
95
|
+
// In Go, accessing fields or calling methods on nil pointers/interfaces panics, so we should throw in TypeScript
|
|
96
|
+
baseType := c.pkg.TypesInfo.TypeOf(exp.X)
|
|
97
|
+
if baseType != nil {
|
|
98
|
+
// Check if the base is a pointer type
|
|
99
|
+
if _, isPtr := baseType.(*types.Pointer); isPtr {
|
|
100
|
+
c.tsw.WriteLiterally("!.")
|
|
101
|
+
} else if _, isInterface := baseType.Underlying().(*types.Interface); isInterface {
|
|
102
|
+
// For interface types, add null assertion since interfaces can be nil
|
|
103
|
+
c.tsw.WriteLiterally("!.")
|
|
104
|
+
} else if callExpr, isCall := exp.X.(*ast.CallExpr); isCall {
|
|
105
|
+
// For function calls that return nullable types, add null assertion
|
|
106
|
+
_ = callExpr // Use the variable to avoid unused error
|
|
107
|
+
c.tsw.WriteLiterally("!.")
|
|
108
|
+
} else {
|
|
109
|
+
// Add .
|
|
110
|
+
c.tsw.WriteLiterally(".")
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
// Add .
|
|
114
|
+
c.tsw.WriteLiterally(".")
|
|
115
|
+
}
|
|
96
116
|
|
|
97
117
|
// Write the field/method name.
|
|
98
118
|
// Pass 'true' to WriteIdent to potentially add '.value' if the field itself
|
package/compiler/expr-type.go
CHANGED
|
@@ -22,13 +22,59 @@ import (
|
|
|
22
22
|
func (c *GoToTSCompiler) WriteTypeExpr(a ast.Expr) {
|
|
23
23
|
// Get type information for the expression and use WriteGoType
|
|
24
24
|
typ := c.pkg.TypesInfo.TypeOf(a)
|
|
25
|
-
c.WriteGoType(typ)
|
|
25
|
+
c.WriteGoType(typ, GoTypeContextGeneral)
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
// writeTypeDescription writes the TypeInfo for a type expr.
|
|
29
29
|
func (c *GoToTSCompiler) writeTypeDescription(typeExpr ast.Expr) {
|
|
30
30
|
switch t := typeExpr.(type) {
|
|
31
31
|
case *ast.Ident:
|
|
32
|
+
// Resolve the identifier to its type
|
|
33
|
+
goType := c.pkg.TypesInfo.TypeOf(t)
|
|
34
|
+
if goType != nil {
|
|
35
|
+
if namedType, isNamed := goType.(*types.Named); isNamed {
|
|
36
|
+
if sig, isFuncSig := namedType.Underlying().(*types.Signature); isFuncSig {
|
|
37
|
+
// It's a named function type (e.g. type MyFunc func())
|
|
38
|
+
c.tsw.WriteLiterally("{")
|
|
39
|
+
c.tsw.WriteLiterally("kind: $.TypeKind.Function, ")
|
|
40
|
+
c.tsw.WriteLiterallyf("name: '%s'", namedType.Obj().Name()) // Use the original defined name
|
|
41
|
+
|
|
42
|
+
// Add params if present
|
|
43
|
+
if sig.Params() != nil && sig.Params().Len() > 0 {
|
|
44
|
+
c.tsw.WriteLiterally(", params: [")
|
|
45
|
+
for i := 0; i < sig.Params().Len(); i++ {
|
|
46
|
+
if i > 0 {
|
|
47
|
+
c.tsw.WriteLiterally(", ")
|
|
48
|
+
}
|
|
49
|
+
paramVar := sig.Params().At(i)
|
|
50
|
+
c.writeTypeInfoObject(paramVar.Type())
|
|
51
|
+
}
|
|
52
|
+
c.tsw.WriteLiterally("]")
|
|
53
|
+
} else {
|
|
54
|
+
c.tsw.WriteLiterally(", params: []")
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Add results if present
|
|
58
|
+
if sig.Results() != nil && sig.Results().Len() > 0 {
|
|
59
|
+
c.tsw.WriteLiterally(", results: [")
|
|
60
|
+
for i := 0; i < sig.Results().Len(); i++ {
|
|
61
|
+
if i > 0 {
|
|
62
|
+
c.tsw.WriteLiterally(", ")
|
|
63
|
+
}
|
|
64
|
+
resultVar := sig.Results().At(i)
|
|
65
|
+
c.writeTypeInfoObject(resultVar.Type())
|
|
66
|
+
}
|
|
67
|
+
c.tsw.WriteLiterally("]")
|
|
68
|
+
} else {
|
|
69
|
+
c.tsw.WriteLiterally(", results: []")
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
c.tsw.WriteLiterally("}")
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
32
78
|
if isPrimitiveType(t.Name) {
|
|
33
79
|
if tsType, ok := GoBuiltinToTypescript(t.Name); ok {
|
|
34
80
|
c.tsw.WriteLiterally("{")
|
|
@@ -112,9 +158,16 @@ func (c *GoToTSCompiler) writeTypeDescription(typeExpr ast.Expr) {
|
|
|
112
158
|
c.tsw.WriteLiterally("kind: $.TypeKind.Function")
|
|
113
159
|
|
|
114
160
|
// Add name if this is a named function type
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
161
|
+
// For an anonymous ast.FuncType, typeExpr is the ast.FuncType itself.
|
|
162
|
+
// We need to check if the type information associated with this AST node
|
|
163
|
+
// points to a *types.Named type.
|
|
164
|
+
resolvedGoType := c.pkg.TypesInfo.TypeOf(typeExpr)
|
|
165
|
+
if resolvedGoType != nil {
|
|
166
|
+
if named, ok := resolvedGoType.(*types.Named); ok {
|
|
167
|
+
// Ensure it's actually a function type that's named
|
|
168
|
+
if _, isFuncSig := named.Underlying().(*types.Signature); isFuncSig {
|
|
169
|
+
c.tsw.WriteLiterallyf(", name: '%s'", named.Obj().Name())
|
|
170
|
+
}
|
|
118
171
|
}
|
|
119
172
|
}
|
|
120
173
|
|
|
@@ -174,6 +227,80 @@ func (c *GoToTSCompiler) writeTypeDescription(typeExpr ast.Expr) {
|
|
|
174
227
|
c.tsw.WriteLiterally("'both'")
|
|
175
228
|
}
|
|
176
229
|
|
|
230
|
+
c.tsw.WriteLiterally("}")
|
|
231
|
+
case *ast.InterfaceType:
|
|
232
|
+
// Handle inline interface types like interface{ Method() string }
|
|
233
|
+
c.tsw.WriteLiterally("{")
|
|
234
|
+
c.tsw.WriteLiterally("kind: $.TypeKind.Interface, ")
|
|
235
|
+
c.tsw.WriteLiterally("methods: [")
|
|
236
|
+
|
|
237
|
+
// Add method signatures for each method in the interface
|
|
238
|
+
if t.Methods != nil && t.Methods.List != nil {
|
|
239
|
+
hasMethod := false
|
|
240
|
+
for _, field := range t.Methods.List {
|
|
241
|
+
// Only process method declarations (not embedded interfaces)
|
|
242
|
+
if len(field.Names) > 0 {
|
|
243
|
+
for _, methodName := range field.Names {
|
|
244
|
+
if hasMethod {
|
|
245
|
+
c.tsw.WriteLiterally(", ")
|
|
246
|
+
}
|
|
247
|
+
hasMethod = true
|
|
248
|
+
|
|
249
|
+
// Write method signature in the same format as writeMethodSignatures
|
|
250
|
+
c.tsw.WriteLiterallyf("{ name: '%s', args: [", methodName.Name)
|
|
251
|
+
|
|
252
|
+
// Get the function type for this method
|
|
253
|
+
if funcType, ok := field.Type.(*ast.FuncType); ok {
|
|
254
|
+
// Add parameters
|
|
255
|
+
if funcType.Params != nil && funcType.Params.List != nil {
|
|
256
|
+
paramIndex := 0
|
|
257
|
+
for _, param := range funcType.Params.List {
|
|
258
|
+
// Each parameter field can declare multiple variables of the same type
|
|
259
|
+
if len(param.Names) > 0 {
|
|
260
|
+
for _, paramName := range param.Names {
|
|
261
|
+
if paramIndex > 0 {
|
|
262
|
+
c.tsw.WriteLiterally(", ")
|
|
263
|
+
}
|
|
264
|
+
c.tsw.WriteLiterallyf("{ name: '%s', type: ", paramName.Name)
|
|
265
|
+
c.writeTypeDescription(param.Type)
|
|
266
|
+
c.tsw.WriteLiterally(" }")
|
|
267
|
+
paramIndex++
|
|
268
|
+
}
|
|
269
|
+
} else {
|
|
270
|
+
// No names, create a generic parameter name
|
|
271
|
+
if paramIndex > 0 {
|
|
272
|
+
c.tsw.WriteLiterally(", ")
|
|
273
|
+
}
|
|
274
|
+
c.tsw.WriteLiterallyf("{ name: '_p%d', type: ", paramIndex)
|
|
275
|
+
c.writeTypeDescription(param.Type)
|
|
276
|
+
c.tsw.WriteLiterally(" }")
|
|
277
|
+
paramIndex++
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
c.tsw.WriteLiterally("], returns: [")
|
|
283
|
+
|
|
284
|
+
// Add return types
|
|
285
|
+
if funcType.Results != nil && funcType.Results.List != nil {
|
|
286
|
+
for i, result := range funcType.Results.List {
|
|
287
|
+
if i > 0 {
|
|
288
|
+
c.tsw.WriteLiterally(", ")
|
|
289
|
+
}
|
|
290
|
+
c.tsw.WriteLiterally("{ type: ")
|
|
291
|
+
c.writeTypeDescription(result.Type)
|
|
292
|
+
c.tsw.WriteLiterally(" }")
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
c.tsw.WriteLiterally("] }")
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
c.tsw.WriteLiterally("]")
|
|
177
304
|
c.tsw.WriteLiterally("}")
|
|
178
305
|
default:
|
|
179
306
|
// For other types, use the string representation
|
package/compiler/expr-value.go
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
package compiler
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import (
|
|
4
|
+
"go/ast"
|
|
5
|
+
)
|
|
4
6
|
|
|
5
7
|
// WriteValueExpr translates a Go abstract syntax tree (AST) expression (`ast.Expr`)
|
|
6
8
|
// that represents a value into its TypeScript value equivalent.
|
|
@@ -50,43 +52,11 @@ func (c *GoToTSCompiler) WriteValueExpr(a ast.Expr) error {
|
|
|
50
52
|
return c.WriteTypeAssertExpr(exp)
|
|
51
53
|
case *ast.IndexExpr:
|
|
52
54
|
return c.WriteIndexExpr(exp)
|
|
55
|
+
case *ast.IndexListExpr:
|
|
56
|
+
// Handle generic function instantiation: f[T1, T2](args) -> f<T1, T2>(args)
|
|
57
|
+
return c.WriteIndexListExpr(exp)
|
|
53
58
|
case *ast.SliceExpr:
|
|
54
|
-
|
|
55
|
-
c.tsw.WriteLiterally("$.goSlice(")
|
|
56
|
-
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
57
|
-
return err
|
|
58
|
-
}
|
|
59
|
-
// low argument
|
|
60
|
-
c.tsw.WriteLiterally(", ")
|
|
61
|
-
if exp.Low != nil {
|
|
62
|
-
if err := c.WriteValueExpr(exp.Low); err != nil {
|
|
63
|
-
return err
|
|
64
|
-
}
|
|
65
|
-
} else {
|
|
66
|
-
c.tsw.WriteLiterally("undefined")
|
|
67
|
-
}
|
|
68
|
-
// high argument
|
|
69
|
-
c.tsw.WriteLiterally(", ")
|
|
70
|
-
if exp.High != nil {
|
|
71
|
-
if err := c.WriteValueExpr(exp.High); err != nil {
|
|
72
|
-
return err
|
|
73
|
-
}
|
|
74
|
-
} else {
|
|
75
|
-
c.tsw.WriteLiterally("undefined")
|
|
76
|
-
}
|
|
77
|
-
// max argument (only for full slice expressions)
|
|
78
|
-
if exp.Slice3 {
|
|
79
|
-
c.tsw.WriteLiterally(", ")
|
|
80
|
-
if exp.Max != nil {
|
|
81
|
-
if err := c.WriteValueExpr(exp.Max); err != nil {
|
|
82
|
-
return err
|
|
83
|
-
}
|
|
84
|
-
} else {
|
|
85
|
-
c.tsw.WriteLiterally("undefined")
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
c.tsw.WriteLiterally(")")
|
|
89
|
-
return nil
|
|
59
|
+
return c.WriteSliceExpr(exp)
|
|
90
60
|
case *ast.ParenExpr:
|
|
91
61
|
// Check if this is a nil pointer to struct type cast: (*struct{})(nil)
|
|
92
62
|
if starExpr, isStarExpr := exp.X.(*ast.StarExpr); isStarExpr {
|