goscript 0.0.45 → 0.0.48

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.
@@ -0,0 +1,424 @@
1
+ package compiler
2
+
3
+ import (
4
+ "fmt"
5
+ "go/ast"
6
+ "go/token"
7
+ "go/types"
8
+ )
9
+
10
+ // writeNilConversion handles type conversions with nil argument
11
+ func (c *GoToTSCompiler) writeNilConversion(exp *ast.CallExpr) (handled bool, err error) {
12
+ if len(exp.Args) != 1 {
13
+ return false, nil
14
+ }
15
+
16
+ nilIdent, isIdent := exp.Args[0].(*ast.Ident)
17
+ if !isIdent || nilIdent.Name != "nil" {
18
+ return false, nil
19
+ }
20
+
21
+ // Handle nil pointer to struct type conversions: (*struct{})(nil)
22
+ if starExpr, isStarExpr := exp.Fun.(*ast.StarExpr); isStarExpr {
23
+ if _, isStructType := starExpr.X.(*ast.StructType); isStructType {
24
+ c.tsw.WriteLiterally("null")
25
+ return true, nil
26
+ }
27
+ }
28
+
29
+ c.tsw.WriteLiterally("null")
30
+ return true, nil
31
+ }
32
+
33
+ // writeArrayTypeConversion handles array type conversions like []rune(string)
34
+ func (c *GoToTSCompiler) writeArrayTypeConversion(exp *ast.CallExpr) (handled bool, err error) {
35
+ arrayType, isArrayType := exp.Fun.(*ast.ArrayType)
36
+ if !isArrayType {
37
+ return false, nil
38
+ }
39
+
40
+ // Check if it's a []rune type
41
+ if ident, isIdent := arrayType.Elt.(*ast.Ident); isIdent && ident.Name == "rune" {
42
+ // Check if the argument is a string
43
+ if len(exp.Args) == 1 {
44
+ arg := exp.Args[0]
45
+ if tv, ok := c.pkg.TypesInfo.Types[arg]; ok && tv.Type != nil {
46
+ if basic, isBasic := tv.Type.Underlying().(*types.Basic); isBasic && basic.Kind() == types.String {
47
+ // Translate []rune(stringValue) to $.stringToRunes(stringValue)
48
+ c.tsw.WriteLiterally("$.stringToRunes(")
49
+ if err := c.WriteValueExpr(arg); err != nil {
50
+ return true, fmt.Errorf("failed to write argument for []rune(string) conversion: %w", err)
51
+ }
52
+ c.tsw.WriteLiterally(")")
53
+ return true, nil
54
+ }
55
+ }
56
+ }
57
+ }
58
+
59
+ // Check if it's a []byte type and the argument is a string
60
+ if eltIdent, ok := arrayType.Elt.(*ast.Ident); ok && eltIdent.Name == "byte" && arrayType.Len == nil {
61
+ if len(exp.Args) == 1 {
62
+ arg := exp.Args[0]
63
+ // Ensure TypesInfo is available and the argument type can be determined
64
+ if tv, typeOk := c.pkg.TypesInfo.Types[arg]; typeOk && tv.Type != nil {
65
+ if c.isStringType(tv.Type) {
66
+ c.tsw.WriteLiterally("$.stringToBytes(")
67
+ if err := c.WriteValueExpr(arg); err != nil {
68
+ return true, fmt.Errorf("failed to write argument for []byte(string) conversion: %w", err)
69
+ }
70
+ c.tsw.WriteLiterally(")")
71
+ return true, nil
72
+ }
73
+ }
74
+ }
75
+ }
76
+
77
+ // Handle general slice type conversions like []T(namedType) where namedType has underlying type []T
78
+ if arrayType.Len == nil && len(exp.Args) == 1 {
79
+ arg := exp.Args[0]
80
+ if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
81
+ // Check if the argument is a named type with a slice underlying type
82
+ if namedArgType, isNamed := argType.(*types.Named); isNamed {
83
+ // Check if the named type has receiver methods (is a wrapper class)
84
+ typeName := namedArgType.Obj().Name()
85
+ if c.hasReceiverMethods(typeName) {
86
+ // Check if the underlying type matches the target slice type
87
+ if sliceUnderlying, isSlice := namedArgType.Underlying().(*types.Slice); isSlice {
88
+ // Get the target slice type
89
+ targetType := c.pkg.TypesInfo.TypeOf(arrayType)
90
+ if targetSliceType, isTargetSlice := targetType.Underlying().(*types.Slice); isTargetSlice {
91
+ // Check if element types are compatible
92
+ if types.Identical(sliceUnderlying.Elem(), targetSliceType.Elem()) {
93
+ // This is a conversion from NamedType to []T where NamedType has underlying []T
94
+ // Use valueOf() to get the underlying slice
95
+ if err := c.WriteValueExpr(arg); err != nil {
96
+ return true, fmt.Errorf("failed to write argument for slice type conversion: %w", err)
97
+ }
98
+ c.tsw.WriteLiterally(".valueOf()")
99
+ return true, nil
100
+ }
101
+ }
102
+ }
103
+ }
104
+ }
105
+ }
106
+ }
107
+
108
+ return false, nil
109
+ }
110
+
111
+ // writeStringConversion handles string() conversion
112
+ func (c *GoToTSCompiler) writeStringConversion(exp *ast.CallExpr) error {
113
+ if len(exp.Args) != 1 {
114
+ return fmt.Errorf("string() conversion expects exactly 1 argument, got %d", len(exp.Args))
115
+ }
116
+
117
+ arg := exp.Args[0]
118
+
119
+ // Case 1: Argument is a string literal string("...")
120
+ if basicLit, isBasicLit := arg.(*ast.BasicLit); isBasicLit && basicLit.Kind == token.STRING {
121
+ // Translate string("...") to "..." (no-op)
122
+ c.WriteBasicLit(basicLit)
123
+ return nil
124
+ }
125
+
126
+ // Case 2: Argument is a rune (int32) or a call to rune()
127
+ innerCall, isCallExpr := arg.(*ast.CallExpr)
128
+
129
+ if isCallExpr {
130
+ // Check if it's a call to rune()
131
+ if innerFunIdent, innerFunIsIdent := innerCall.Fun.(*ast.Ident); innerFunIsIdent && innerFunIdent.String() == "rune" {
132
+ // Translate string(rune(val)) to $.runeOrStringToString(val)
133
+ if len(innerCall.Args) == 1 {
134
+ c.tsw.WriteLiterally("$.runeOrStringToString(")
135
+ if err := c.WriteValueExpr(innerCall.Args[0]); err != nil {
136
+ return fmt.Errorf("failed to write argument for string(rune) conversion: %w", err)
137
+ }
138
+ c.tsw.WriteLiterally(")")
139
+ return nil
140
+ }
141
+ }
142
+ }
143
+
144
+ // Handle direct string(int32) conversion
145
+ if tv, ok := c.pkg.TypesInfo.Types[arg]; ok {
146
+ // Case 3a: Argument is already a string - no-op
147
+ if c.isStringType(tv.Type) {
148
+ // Translate string(stringValue) to stringValue (no-op)
149
+ if err := c.WriteValueExpr(arg); err != nil {
150
+ return fmt.Errorf("failed to write argument for string(string) no-op conversion: %w", err)
151
+ }
152
+ return nil
153
+ }
154
+
155
+ if basic, isBasic := tv.Type.Underlying().(*types.Basic); isBasic && (basic.Kind() == types.Int32 || basic.Kind() == types.UntypedRune) {
156
+ // Translate string(rune_val) to $.runeOrStringToString(rune_val)
157
+ c.tsw.WriteLiterally("$.runeOrStringToString(")
158
+ if err := c.WriteValueExpr(arg); err != nil {
159
+ return fmt.Errorf("failed to write argument for string(int32) conversion: %w", err)
160
+ }
161
+ c.tsw.WriteLiterally(")")
162
+ return nil
163
+ }
164
+
165
+ // Case 3: Argument is a slice of runes or bytes string([]rune{...}) or string([]byte{...})
166
+ if c.isByteSliceType(tv.Type) {
167
+ c.tsw.WriteLiterally("$.bytesToString(")
168
+ if err := c.WriteValueExpr(arg); err != nil {
169
+ return fmt.Errorf("failed to write argument for string([]byte) conversion: %w", err)
170
+ }
171
+ c.tsw.WriteLiterally(")")
172
+ return nil
173
+ }
174
+ if c.isRuneSliceType(tv.Type) {
175
+ c.tsw.WriteLiterally("$.runesToString(")
176
+ if err := c.WriteValueExpr(arg); err != nil {
177
+ return fmt.Errorf("failed to write argument for string([]rune) conversion: %w", err)
178
+ }
179
+ c.tsw.WriteLiterally(")")
180
+ return nil
181
+ }
182
+
183
+ // Case 4: Argument is a generic type parameter (e.g., string | []byte)
184
+ if typeParam, isTypeParam := tv.Type.(*types.TypeParam); isTypeParam {
185
+ // Check if this is a []byte | string union constraint
186
+ constraint := typeParam.Constraint()
187
+ if constraint != nil {
188
+ // For now, assume any type parameter that could be string or []byte needs the helper
189
+ // This is a heuristic - in the future we could parse the constraint more precisely
190
+ c.tsw.WriteLiterally("$.genericBytesOrStringToString(")
191
+ if err := c.WriteValueExpr(arg); err != nil {
192
+ return fmt.Errorf("failed to write argument for string(generic) conversion: %w", err)
193
+ }
194
+ c.tsw.WriteLiterally(")")
195
+ return nil
196
+ }
197
+ }
198
+ }
199
+
200
+ return fmt.Errorf("unhandled string conversion: %s", exp.Fun)
201
+ }
202
+
203
+ // writeTypeConversion handles named type conversions
204
+ func (c *GoToTSCompiler) writeTypeConversion(exp *ast.CallExpr, funIdent *ast.Ident) (handled bool, err error) {
205
+ // Check if this is a type conversion to a function type
206
+ if funIdent == nil {
207
+ return false, nil
208
+ }
209
+
210
+ if obj := c.pkg.TypesInfo.Uses[funIdent]; obj != nil {
211
+ // Check if the object is a type name
212
+ if typeName, isType := obj.(*types.TypeName); isType {
213
+ // Make sure we have exactly one argument
214
+ if len(exp.Args) == 1 {
215
+ arg := exp.Args[0]
216
+
217
+ // Check if we're converting FROM a type with receiver methods TO its underlying type
218
+ if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
219
+ if namedArgType, isNamed := argType.(*types.Named); isNamed {
220
+ argTypeName := namedArgType.Obj().Name()
221
+ // Check if the argument type has receiver methods
222
+ if c.hasReceiverMethods(argTypeName) {
223
+ // Check if we're converting to the underlying type
224
+ targetType := typeName.Type()
225
+ underlyingType := namedArgType.Underlying()
226
+ if types.Identical(targetType, underlyingType) {
227
+ // This is a conversion from a type with methods to its underlying type
228
+ // Use valueOf() instead of TypeScript cast
229
+ if err := c.WriteValueExpr(arg); err != nil {
230
+ return true, fmt.Errorf("failed to write argument for valueOf conversion: %w", err)
231
+ }
232
+ c.tsw.WriteLiterally(".valueOf()")
233
+ return true, nil
234
+ }
235
+ }
236
+ }
237
+ }
238
+
239
+ // Check if this is a function type
240
+ if _, isFuncType := typeName.Type().Underlying().(*types.Signature); isFuncType {
241
+ // For function types, we need to add a __goTypeName property
242
+ c.tsw.WriteLiterally("Object.assign(")
243
+
244
+ // Write the argument first
245
+ if err := c.WriteValueExpr(exp.Args[0]); err != nil {
246
+ return true, fmt.Errorf("failed to write argument for function type cast: %w", err)
247
+ }
248
+
249
+ // Add the __goTypeName property with the function type name
250
+ c.tsw.WriteLiterallyf(", { __goTypeName: '%s' })", funIdent.String())
251
+ return true, nil
252
+ } else {
253
+ // Check if this type has receiver methods
254
+ if c.hasReceiverMethods(funIdent.String()) {
255
+ // For types with methods, use class constructor
256
+ c.tsw.WriteLiterally("new ")
257
+ c.tsw.WriteLiterally(funIdent.String())
258
+ c.tsw.WriteLiterally("(")
259
+ if err := c.WriteValueExpr(exp.Args[0]); err != nil {
260
+ return true, fmt.Errorf("failed to write argument for type constructor: %w", err)
261
+ }
262
+ c.tsw.WriteLiterally(")")
263
+ return true, nil
264
+ } else {
265
+ // For non-function types without methods, use the TypeScript "as" operator
266
+ c.tsw.WriteLiterally("(")
267
+ if err := c.WriteValueExpr(exp.Args[0]); err != nil {
268
+ return true, fmt.Errorf("failed to write argument for type cast: %w", err)
269
+ }
270
+
271
+ // Then use the TypeScript "as" operator with the mapped type name
272
+ c.tsw.WriteLiterally(" as ")
273
+ c.WriteGoType(typeName.Type(), GoTypeContextGeneral)
274
+ c.tsw.WriteLiterally(")")
275
+ return true, nil
276
+ }
277
+ }
278
+ }
279
+ }
280
+ }
281
+
282
+ return false, nil
283
+ }
284
+
285
+ // writeIntConversion handles int() conversion
286
+ func (c *GoToTSCompiler) writeIntConversion(exp *ast.CallExpr) error {
287
+ if len(exp.Args) != 1 {
288
+ return fmt.Errorf("int() conversion expects exactly 1 argument, got %d", len(exp.Args))
289
+ }
290
+
291
+ arg := exp.Args[0]
292
+
293
+ // Check if we're converting FROM a type with receiver methods TO int
294
+ if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
295
+ if namedArgType, isNamed := argType.(*types.Named); isNamed {
296
+ argTypeName := namedArgType.Obj().Name()
297
+ // Check if the argument type has receiver methods
298
+ if c.hasReceiverMethods(argTypeName) {
299
+ // Check if we're converting to int (the underlying type)
300
+ if types.Identical(types.Typ[types.Int], namedArgType.Underlying()) {
301
+ // This is a conversion from a type with methods to int
302
+ // Use valueOf() instead of $.int()
303
+ if err := c.WriteValueExpr(arg); err != nil {
304
+ return fmt.Errorf("failed to write argument for valueOf conversion: %w", err)
305
+ }
306
+ c.tsw.WriteLiterally(".valueOf()")
307
+ return nil
308
+ }
309
+ }
310
+ }
311
+ }
312
+
313
+ // Default case: Translate int(value) to $.int(value)
314
+ c.tsw.WriteLiterally("$.int(")
315
+ if err := c.WriteValueExpr(exp.Args[0]); err != nil {
316
+ return fmt.Errorf("failed to write argument for int() conversion: %w", err)
317
+ }
318
+ c.tsw.WriteLiterally(")")
319
+ return nil
320
+ }
321
+
322
+ // writeQualifiedTypeConversion handles qualified type conversions like os.FileMode(value)
323
+ func (c *GoToTSCompiler) writeQualifiedTypeConversion(exp *ast.CallExpr, selectorExpr *ast.SelectorExpr) (handled bool, err error) {
324
+ // Check if this is a type conversion by looking up the selector in the type info
325
+ if obj := c.pkg.TypesInfo.Uses[selectorExpr.Sel]; obj != nil {
326
+ // Check if the object is a type name
327
+ if typeName, isType := obj.(*types.TypeName); isType {
328
+ // Make sure we have exactly one argument
329
+ if len(exp.Args) == 1 {
330
+ arg := exp.Args[0]
331
+
332
+ // Check if we're converting FROM a type with receiver methods TO its underlying type
333
+ if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
334
+ if namedArgType, isNamed := argType.(*types.Named); isNamed {
335
+ argTypeName := namedArgType.Obj().Name()
336
+ // Check if the argument type has receiver methods
337
+ if c.hasReceiverMethods(argTypeName) {
338
+ // Check if we're converting to the underlying type
339
+ targetType := typeName.Type()
340
+ underlyingType := namedArgType.Underlying()
341
+ if types.Identical(targetType, underlyingType) {
342
+ // This is a conversion from a type with methods to its underlying type
343
+ // Use valueOf() instead of TypeScript cast
344
+ if err := c.WriteValueExpr(arg); err != nil {
345
+ return true, fmt.Errorf("failed to write argument for valueOf conversion: %w", err)
346
+ }
347
+ c.tsw.WriteLiterally(".valueOf()")
348
+ return true, nil
349
+ }
350
+ }
351
+ }
352
+ }
353
+
354
+ // Check if this is a function type
355
+ if _, isFuncType := typeName.Type().Underlying().(*types.Signature); isFuncType {
356
+ // For function types, we need to add a __goTypeName property
357
+ c.tsw.WriteLiterally("Object.assign(")
358
+
359
+ // Write the argument first
360
+ if err := c.WriteValueExpr(exp.Args[0]); err != nil {
361
+ return true, fmt.Errorf("failed to write argument for function type cast: %w", err)
362
+ }
363
+
364
+ // Add the __goTypeName property with the function type name
365
+ c.tsw.WriteLiterallyf(", { __goTypeName: '%s' })", selectorExpr.Sel.Name)
366
+ return true, nil
367
+ } else {
368
+ // Determine if this type should use constructor syntax
369
+ shouldUseConstructor := false
370
+
371
+ // Check if it's a type alias (like os.FileMode)
372
+ if alias, isAlias := typeName.Type().(*types.Alias); isAlias {
373
+ // For type aliases, check the underlying type
374
+ if _, isInterface := alias.Underlying().(*types.Interface); !isInterface {
375
+ if _, isStruct := alias.Underlying().(*types.Struct); !isStruct {
376
+ // For non-struct, non-interface type aliases, use constructor
377
+ shouldUseConstructor = true
378
+ }
379
+ }
380
+ } else if namedType, isNamed := typeName.Type().(*types.Named); isNamed {
381
+ // For named types, check if they have receiver methods in the current package
382
+ // or if they follow the pattern of non-struct, non-interface named types
383
+ if c.hasReceiverMethods(selectorExpr.Sel.Name) {
384
+ shouldUseConstructor = true
385
+ } else if _, isInterface := namedType.Underlying().(*types.Interface); !isInterface {
386
+ if _, isStruct := namedType.Underlying().(*types.Struct); !isStruct {
387
+ // For non-struct, non-interface named types, use constructor
388
+ shouldUseConstructor = true
389
+ }
390
+ }
391
+ }
392
+
393
+ if shouldUseConstructor {
394
+ // For types that should use constructors, use class constructor
395
+ c.tsw.WriteLiterally("new ")
396
+ if err := c.WriteSelectorExpr(selectorExpr); err != nil {
397
+ return true, fmt.Errorf("failed to write qualified type name: %w", err)
398
+ }
399
+ c.tsw.WriteLiterally("(")
400
+ if err := c.WriteValueExpr(exp.Args[0]); err != nil {
401
+ return true, fmt.Errorf("failed to write argument for type constructor: %w", err)
402
+ }
403
+ c.tsw.WriteLiterally(")")
404
+ return true, nil
405
+ } else {
406
+ // For types that don't need constructors, use the TypeScript "as" operator
407
+ c.tsw.WriteLiterally("(")
408
+ if err := c.WriteValueExpr(exp.Args[0]); err != nil {
409
+ return true, fmt.Errorf("failed to write argument for type cast: %w", err)
410
+ }
411
+
412
+ // Then use the TypeScript "as" operator with the mapped type name
413
+ c.tsw.WriteLiterally(" as ")
414
+ c.WriteGoType(typeName.Type(), GoTypeContextGeneral)
415
+ c.tsw.WriteLiterally(")")
416
+ return true, nil
417
+ }
418
+ }
419
+ }
420
+ }
421
+ }
422
+
423
+ return false, nil
424
+ }