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.
- package/README.md +198 -504
- package/compiler/compiler.go +20 -70
- package/compiler/decl.go +52 -38
- package/compiler/expr-call-async.go +101 -0
- package/compiler/expr-call-builtins.go +133 -0
- package/compiler/expr-call-helpers.go +138 -0
- package/compiler/expr-call-make.go +568 -0
- package/compiler/expr-call-type-conversion.go +424 -0
- package/compiler/expr-call.go +59 -1305
- package/compiler/expr.go +126 -4
- package/compiler/spec-struct.go +22 -9
- package/compiler/spec.go +53 -118
- package/compiler/type.go +51 -0
- package/dist/gs/builtin/builtin.d.ts +3 -1
- package/dist/gs/builtin/builtin.js +6 -0
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/io/fs/fs.d.ts +12 -1
- package/dist/gs/io/fs/fs.js +106 -30
- package/dist/gs/io/fs/fs.js.map +1 -1
- package/dist/gs/os/types_js.gs.d.ts +1 -3
- package/dist/gs/os/types_js.gs.js +2 -1
- package/dist/gs/os/types_js.gs.js.map +1 -1
- package/dist/gs/os/types_unix.gs.js +2 -2
- package/dist/gs/os/types_unix.gs.js.map +1 -1
- package/gs/builtin/builtin.ts +7 -2
- package/gs/io/fs/fs.ts +100 -31
- package/gs/io/fs/godoc.txt +370 -17
- package/gs/os/types_js.gs.ts +2 -2
- package/gs/os/types_unix.gs.ts +2 -2
- package/package.json +1 -1
package/compiler/compiler.go
CHANGED
|
@@ -374,68 +374,6 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
|
|
|
374
374
|
}
|
|
375
375
|
defer indexFile.Close() //nolint:errcheck
|
|
376
376
|
|
|
377
|
-
// Collect exported symbols from all files in the package
|
|
378
|
-
var exportedSymbols []string
|
|
379
|
-
|
|
380
|
-
// Iterate through all syntax files to find exported symbols
|
|
381
|
-
for i, syntax := range c.pkg.Syntax {
|
|
382
|
-
fileName := c.pkg.CompiledGoFiles[i]
|
|
383
|
-
baseFileName := filepath.Base(fileName)
|
|
384
|
-
gsFileName := strings.TrimSuffix(baseFileName, ".go") + ".gs"
|
|
385
|
-
|
|
386
|
-
// Only include this file if it was compiled (in our compiledFiles list)
|
|
387
|
-
fileWasCompiled := false
|
|
388
|
-
for _, compiledFile := range compiledFiles {
|
|
389
|
-
if compiledFile == gsFileName {
|
|
390
|
-
fileWasCompiled = true
|
|
391
|
-
break
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
if !fileWasCompiled {
|
|
395
|
-
continue
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// Analyze declarations in this file to find exported symbols
|
|
399
|
-
for _, decl := range syntax.Decls {
|
|
400
|
-
switch d := decl.(type) {
|
|
401
|
-
case *ast.FuncDecl:
|
|
402
|
-
// Only include top-level functions (not methods)
|
|
403
|
-
if d.Recv == nil && d.Name.IsExported() {
|
|
404
|
-
exportedSymbols = append(exportedSymbols, d.Name.Name)
|
|
405
|
-
}
|
|
406
|
-
case *ast.GenDecl:
|
|
407
|
-
for _, spec := range d.Specs {
|
|
408
|
-
switch s := spec.(type) {
|
|
409
|
-
case *ast.TypeSpec:
|
|
410
|
-
if s.Name.IsExported() {
|
|
411
|
-
exportedSymbols = append(exportedSymbols, s.Name.Name)
|
|
412
|
-
}
|
|
413
|
-
case *ast.ValueSpec:
|
|
414
|
-
for _, name := range s.Names {
|
|
415
|
-
if name.IsExported() {
|
|
416
|
-
exportedSymbols = append(exportedSymbols, name.Name)
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// Remove duplicates and sort
|
|
426
|
-
symbolMap := make(map[string]bool)
|
|
427
|
-
for _, symbol := range exportedSymbols {
|
|
428
|
-
symbolMap[symbol] = true
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
var uniqueSymbols []string
|
|
432
|
-
for symbol := range symbolMap {
|
|
433
|
-
uniqueSymbols = append(uniqueSymbols, symbol)
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
// Sort for consistent output
|
|
437
|
-
sort.Strings(uniqueSymbols)
|
|
438
|
-
|
|
439
377
|
// Write selective re-exports for each compiled file
|
|
440
378
|
for _, fileName := range compiledFiles {
|
|
441
379
|
// Check if this is a protobuf file
|
|
@@ -452,7 +390,8 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
|
|
|
452
390
|
}
|
|
453
391
|
|
|
454
392
|
// Find which symbols this file exports
|
|
455
|
-
var
|
|
393
|
+
var valueSymbols []string
|
|
394
|
+
var typeSymbols []string
|
|
456
395
|
|
|
457
396
|
// Find the corresponding syntax file
|
|
458
397
|
for i, syntax := range c.pkg.Syntax {
|
|
@@ -469,19 +408,21 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
|
|
|
469
408
|
switch d := decl.(type) {
|
|
470
409
|
case *ast.FuncDecl:
|
|
471
410
|
if d.Recv == nil && d.Name.IsExported() {
|
|
472
|
-
|
|
411
|
+
valueSymbols = append(valueSymbols, d.Name.Name)
|
|
473
412
|
}
|
|
474
413
|
case *ast.GenDecl:
|
|
475
414
|
for _, spec := range d.Specs {
|
|
476
415
|
switch s := spec.(type) {
|
|
477
416
|
case *ast.TypeSpec:
|
|
478
417
|
if s.Name.IsExported() {
|
|
479
|
-
|
|
418
|
+
// All type declarations (interfaces, structs, type definitions, type aliases)
|
|
419
|
+
// become TypeScript types and must be exported with "export type"
|
|
420
|
+
typeSymbols = append(typeSymbols, s.Name.Name)
|
|
480
421
|
}
|
|
481
422
|
case *ast.ValueSpec:
|
|
482
423
|
for _, name := range s.Names {
|
|
483
424
|
if name.IsExported() {
|
|
484
|
-
|
|
425
|
+
valueSymbols = append(valueSymbols, name.Name)
|
|
485
426
|
}
|
|
486
427
|
}
|
|
487
428
|
}
|
|
@@ -491,11 +432,20 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
|
|
|
491
432
|
break
|
|
492
433
|
}
|
|
493
434
|
|
|
494
|
-
// Write
|
|
495
|
-
if len(
|
|
496
|
-
sort.Strings(
|
|
435
|
+
// Write exports if this file has exported symbols
|
|
436
|
+
if len(valueSymbols) > 0 {
|
|
437
|
+
sort.Strings(valueSymbols)
|
|
497
438
|
exportLine := fmt.Sprintf("export { %s } from \"./%s.js\"\n",
|
|
498
|
-
strings.Join(
|
|
439
|
+
strings.Join(valueSymbols, ", "), fileName)
|
|
440
|
+
if _, err := indexFile.WriteString(exportLine); err != nil {
|
|
441
|
+
return err
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if len(typeSymbols) > 0 {
|
|
446
|
+
sort.Strings(typeSymbols)
|
|
447
|
+
exportLine := fmt.Sprintf("export type { %s } from \"./%s.js\"\n",
|
|
448
|
+
strings.Join(typeSymbols, ", "), fileName)
|
|
499
449
|
if _, err := indexFile.WriteString(exportLine); err != nil {
|
|
500
450
|
return err
|
|
501
451
|
}
|
package/compiler/decl.go
CHANGED
|
@@ -142,6 +142,49 @@ func (c *GoToTSCompiler) WriteFuncDeclAsFunction(decl *ast.FuncDecl) error {
|
|
|
142
142
|
//
|
|
143
143
|
// This function assumes it is called only for `FuncDecl` nodes that are methods.
|
|
144
144
|
func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
|
|
145
|
+
_, err := c.writeMethodSignature(decl)
|
|
146
|
+
if err != nil {
|
|
147
|
+
return err
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return c.writeMethodBodyWithReceiverBinding(decl, "this")
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// writeNamedReturnDeclarations generates TypeScript variable declarations for named return parameters.
|
|
154
|
+
// It declares each named return variable with its appropriate type and zero value.
|
|
155
|
+
func (c *GoToTSCompiler) writeNamedReturnDeclarations(results *ast.FieldList) error {
|
|
156
|
+
if results == nil {
|
|
157
|
+
return nil
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
for _, field := range results.List {
|
|
161
|
+
for _, name := range field.Names {
|
|
162
|
+
c.tsw.WriteLiterallyf("let %s: ", c.sanitizeIdentifier(name.Name))
|
|
163
|
+
c.WriteTypeExpr(field.Type)
|
|
164
|
+
c.tsw.WriteLiterally(" = ")
|
|
165
|
+
c.WriteZeroValueForType(c.pkg.TypesInfo.TypeOf(field.Type))
|
|
166
|
+
c.tsw.WriteLine("")
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return nil
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// hasNamedReturns checks if a function type has any named return parameters.
|
|
173
|
+
func (c *GoToTSCompiler) hasNamedReturns(results *ast.FieldList) bool {
|
|
174
|
+
if results == nil {
|
|
175
|
+
return false
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
for _, field := range results.List {
|
|
179
|
+
if len(field.Names) > 0 {
|
|
180
|
+
return true
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return false
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// writeMethodSignature writes the TypeScript method signature including async, public modifiers, name, parameters, and return type
|
|
187
|
+
func (c *GoToTSCompiler) writeMethodSignature(decl *ast.FuncDecl) (bool, error) {
|
|
145
188
|
if decl.Doc != nil {
|
|
146
189
|
c.WriteDoc(decl.Doc)
|
|
147
190
|
}
|
|
@@ -162,11 +205,10 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
|
|
|
162
205
|
|
|
163
206
|
// Keep original Go casing for method names
|
|
164
207
|
if err := c.WriteValueExpr(decl.Name); err != nil { // Method name is a value identifier
|
|
165
|
-
return err
|
|
208
|
+
return isAsync, err
|
|
166
209
|
}
|
|
167
210
|
|
|
168
211
|
// Write signature (parameters and return type)
|
|
169
|
-
// We adapt the logic from WriteFuncType here, but without the 'function' keyword
|
|
170
212
|
funcType := decl.Type
|
|
171
213
|
c.tsw.WriteLiterally("(")
|
|
172
214
|
if funcType.Params != nil {
|
|
@@ -208,8 +250,13 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
|
|
|
208
250
|
}
|
|
209
251
|
|
|
210
252
|
c.tsw.WriteLiterally(" ")
|
|
253
|
+
return isAsync, nil
|
|
254
|
+
}
|
|
211
255
|
|
|
212
|
-
|
|
256
|
+
// writeMethodBodyWithReceiverBinding writes the method body with optional receiver binding
|
|
257
|
+
// receiverTarget should be "this" for struct methods or "this._value" for named type methods
|
|
258
|
+
func (c *GoToTSCompiler) writeMethodBodyWithReceiverBinding(decl *ast.FuncDecl, receiverTarget string) error {
|
|
259
|
+
// Bind receiver name conditionally
|
|
213
260
|
if recvField := decl.Recv.List[0]; len(recvField.Names) > 0 {
|
|
214
261
|
recvName := recvField.Names[0].Name
|
|
215
262
|
if recvName != "_" {
|
|
@@ -225,7 +272,7 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
|
|
|
225
272
|
if needsReceiverBinding {
|
|
226
273
|
// Sanitize the receiver name to avoid conflicts with TypeScript reserved words
|
|
227
274
|
sanitizedRecvName := c.sanitizeIdentifier(recvName)
|
|
228
|
-
c.tsw.WriteLinef("const %s =
|
|
275
|
+
c.tsw.WriteLinef("const %s = %s", sanitizedRecvName, receiverTarget)
|
|
229
276
|
}
|
|
230
277
|
|
|
231
278
|
// Add using statement if needed
|
|
@@ -233,7 +280,7 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
|
|
|
233
280
|
if c.analysis.IsInAsyncFunction(decl) {
|
|
234
281
|
c.tsw.WriteLine("await using __defer = new $.AsyncDisposableStack();")
|
|
235
282
|
} else {
|
|
236
|
-
c.tsw.WriteLine("using
|
|
283
|
+
c.tsw.WriteLine("using __defer = new $.DisposableStack();")
|
|
237
284
|
}
|
|
238
285
|
}
|
|
239
286
|
|
|
@@ -261,36 +308,3 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
|
|
|
261
308
|
|
|
262
309
|
return nil
|
|
263
310
|
}
|
|
264
|
-
|
|
265
|
-
// writeNamedReturnDeclarations generates TypeScript variable declarations for named return parameters.
|
|
266
|
-
// It declares each named return variable with its appropriate type and zero value.
|
|
267
|
-
func (c *GoToTSCompiler) writeNamedReturnDeclarations(results *ast.FieldList) error {
|
|
268
|
-
if results == nil {
|
|
269
|
-
return nil
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
for _, field := range results.List {
|
|
273
|
-
for _, name := range field.Names {
|
|
274
|
-
c.tsw.WriteLiterallyf("let %s: ", c.sanitizeIdentifier(name.Name))
|
|
275
|
-
c.WriteTypeExpr(field.Type)
|
|
276
|
-
c.tsw.WriteLiterally(" = ")
|
|
277
|
-
c.WriteZeroValueForType(c.pkg.TypesInfo.TypeOf(field.Type))
|
|
278
|
-
c.tsw.WriteLine("")
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
return nil
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// hasNamedReturns checks if a function type has any named return parameters.
|
|
285
|
-
func (c *GoToTSCompiler) hasNamedReturns(results *ast.FieldList) bool {
|
|
286
|
-
if results == nil {
|
|
287
|
-
return false
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
for _, field := range results.List {
|
|
291
|
-
if len(field.Names) > 0 {
|
|
292
|
-
return true
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
return false
|
|
296
|
-
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"go/ast"
|
|
5
|
+
"go/types"
|
|
6
|
+
"strings"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// writeAsyncCall writes the await prefix for async function calls
|
|
10
|
+
func (c *GoToTSCompiler) writeAsyncCall(exp *ast.CallExpr, funIdent *ast.Ident) bool {
|
|
11
|
+
if funIdent == nil {
|
|
12
|
+
return false
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Check if this is an async function call
|
|
16
|
+
if obj := c.pkg.TypesInfo.Uses[funIdent]; obj != nil && c.analysis.IsAsyncFunc(obj) {
|
|
17
|
+
c.tsw.WriteLiterally("await ")
|
|
18
|
+
return true
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return false
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// writeAsyncMethodCall writes the await prefix for async method calls
|
|
25
|
+
func (c *GoToTSCompiler) writeAsyncMethodCall(exp *ast.CallExpr) bool {
|
|
26
|
+
selExpr, ok := exp.Fun.(*ast.SelectorExpr)
|
|
27
|
+
if !ok {
|
|
28
|
+
return false
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Check if this is a method call on a variable (e.g., mu.Lock())
|
|
32
|
+
ident, ok := selExpr.X.(*ast.Ident)
|
|
33
|
+
if !ok {
|
|
34
|
+
return false
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Get the type of the receiver
|
|
38
|
+
obj := c.pkg.TypesInfo.Uses[ident]
|
|
39
|
+
if obj == nil {
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
varObj, ok := obj.(*types.Var)
|
|
44
|
+
if !ok {
|
|
45
|
+
return false
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Get the type name and package
|
|
49
|
+
namedType, ok := varObj.Type().(*types.Named)
|
|
50
|
+
if !ok {
|
|
51
|
+
return false
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
typeName := namedType.Obj().Name()
|
|
55
|
+
methodName := selExpr.Sel.Name
|
|
56
|
+
|
|
57
|
+
// Check if the type is from an imported package
|
|
58
|
+
typePkg := namedType.Obj().Pkg()
|
|
59
|
+
if typePkg == nil || typePkg == c.pkg.Types {
|
|
60
|
+
return false
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Use the actual package name from the type information
|
|
64
|
+
pkgName := typePkg.Name()
|
|
65
|
+
|
|
66
|
+
// Check if this method is async based on metadata
|
|
67
|
+
if c.analysis.IsMethodAsync(pkgName, typeName, methodName) {
|
|
68
|
+
c.tsw.WriteLiterally("await ")
|
|
69
|
+
return true
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return false
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// addNonNullAssertion adds ! for function calls that might return null
|
|
76
|
+
func (c *GoToTSCompiler) addNonNullAssertion(expFun ast.Expr) {
|
|
77
|
+
if funType := c.pkg.TypesInfo.TypeOf(expFun); funType != nil {
|
|
78
|
+
if _, ok := funType.Underlying().(*types.Signature); ok {
|
|
79
|
+
// Check if this is a function parameter identifier that needs not-null assertion
|
|
80
|
+
if ident, isIdent := expFun.(*ast.Ident); isIdent {
|
|
81
|
+
// Check if this identifier is a function parameter
|
|
82
|
+
if obj := c.pkg.TypesInfo.Uses[ident]; obj != nil {
|
|
83
|
+
if _, isVar := obj.(*types.Var); isVar {
|
|
84
|
+
// This is a variable (including function parameters)
|
|
85
|
+
// Function parameters that are function types need ! assertion
|
|
86
|
+
c.tsw.WriteLiterally("!")
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
} else if _, isNamed := funType.(*types.Named); isNamed {
|
|
90
|
+
c.tsw.WriteLiterally("!")
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
// Check if the function type is nullable (e.g., func(...) | null)
|
|
94
|
+
// This handles cases where a function call returns a nullable function
|
|
95
|
+
funTypeStr := funType.String()
|
|
96
|
+
if strings.Contains(funTypeStr, "| null") || strings.Contains(funTypeStr, "null |") {
|
|
97
|
+
c.tsw.WriteLiterally("!")
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"go/ast"
|
|
6
|
+
"go/token"
|
|
7
|
+
|
|
8
|
+
"github.com/pkg/errors"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// writeBuiltinFunction handles built-in Go functions
|
|
12
|
+
func (c *GoToTSCompiler) writeBuiltinFunction(exp *ast.CallExpr, funName string) (handled bool, err error) {
|
|
13
|
+
switch funName {
|
|
14
|
+
case "panic":
|
|
15
|
+
c.tsw.WriteLiterally("$.panic")
|
|
16
|
+
return true, nil
|
|
17
|
+
case "println":
|
|
18
|
+
c.tsw.WriteLiterally("console.log")
|
|
19
|
+
return true, nil
|
|
20
|
+
case "len":
|
|
21
|
+
if len(exp.Args) != 1 {
|
|
22
|
+
return true, errors.Errorf("unhandled len call with incorrect number of arguments: %d != 1", len(exp.Args))
|
|
23
|
+
}
|
|
24
|
+
c.tsw.WriteLiterally("$.len")
|
|
25
|
+
return true, nil
|
|
26
|
+
case "cap":
|
|
27
|
+
if len(exp.Args) != 1 {
|
|
28
|
+
return true, errors.Errorf("unhandled cap call with incorrect number of arguments: %d != 1", len(exp.Args))
|
|
29
|
+
}
|
|
30
|
+
c.tsw.WriteLiterally("$.cap")
|
|
31
|
+
return true, nil
|
|
32
|
+
case "new":
|
|
33
|
+
if len(exp.Args) != 1 {
|
|
34
|
+
return true, errors.Errorf("unhandled new call with incorrect number of arguments: %d != 1", len(exp.Args))
|
|
35
|
+
}
|
|
36
|
+
c.tsw.WriteLiterally("new ")
|
|
37
|
+
c.WriteTypeExpr(exp.Args[0]) // This should write the TypeScript type T_ts
|
|
38
|
+
c.tsw.WriteLiterally("()")
|
|
39
|
+
return true, nil
|
|
40
|
+
case "delete":
|
|
41
|
+
if len(exp.Args) != 2 {
|
|
42
|
+
return true, errors.Errorf("unhandled delete call with incorrect number of arguments: %d != 2", len(exp.Args))
|
|
43
|
+
}
|
|
44
|
+
c.tsw.WriteLiterally("$.deleteMapEntry")
|
|
45
|
+
return true, nil
|
|
46
|
+
case "copy":
|
|
47
|
+
if len(exp.Args) != 2 {
|
|
48
|
+
return true, errors.Errorf("unhandled copy call with incorrect number of arguments: %d != 2", len(exp.Args))
|
|
49
|
+
}
|
|
50
|
+
c.tsw.WriteLiterally("$.copy")
|
|
51
|
+
return true, nil
|
|
52
|
+
case "recover":
|
|
53
|
+
if len(exp.Args) != 0 {
|
|
54
|
+
return true, errors.Errorf("unhandled recover call with incorrect number of arguments: %d != 0", len(exp.Args))
|
|
55
|
+
}
|
|
56
|
+
c.tsw.WriteLiterally("$.recover")
|
|
57
|
+
return true, nil
|
|
58
|
+
case "make":
|
|
59
|
+
return true, c.WriteCallExprMake(exp)
|
|
60
|
+
case "string":
|
|
61
|
+
return true, c.writeStringConversion(exp)
|
|
62
|
+
case "close":
|
|
63
|
+
if len(exp.Args) != 1 {
|
|
64
|
+
return true, errors.New("unhandled close call with incorrect number of arguments")
|
|
65
|
+
}
|
|
66
|
+
if err := c.WriteValueExpr(exp.Args[0]); err != nil {
|
|
67
|
+
return true, fmt.Errorf("failed to write channel in close call: %w", err)
|
|
68
|
+
}
|
|
69
|
+
c.tsw.WriteLiterally(".close()")
|
|
70
|
+
return true, nil
|
|
71
|
+
case "append":
|
|
72
|
+
return true, c.writeAppendCall(exp)
|
|
73
|
+
case "byte":
|
|
74
|
+
if len(exp.Args) != 1 {
|
|
75
|
+
return true, errors.Errorf("unhandled byte call with incorrect number of arguments: %d != 1", len(exp.Args))
|
|
76
|
+
}
|
|
77
|
+
c.tsw.WriteLiterally("$.byte(")
|
|
78
|
+
if err := c.WriteValueExpr(exp.Args[0]); err != nil {
|
|
79
|
+
return true, fmt.Errorf("failed to write argument for byte() conversion: %w", err)
|
|
80
|
+
}
|
|
81
|
+
c.tsw.WriteLiterally(")")
|
|
82
|
+
return true, nil
|
|
83
|
+
case "int":
|
|
84
|
+
return true, c.writeIntConversion(exp)
|
|
85
|
+
default:
|
|
86
|
+
return false, nil
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// writeAppendCall handles append() function calls
|
|
91
|
+
func (c *GoToTSCompiler) writeAppendCall(exp *ast.CallExpr) error {
|
|
92
|
+
if len(exp.Args) < 1 {
|
|
93
|
+
return errors.New("unhandled append call with incorrect number of arguments")
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
c.tsw.WriteLiterally("$.append(")
|
|
97
|
+
// The first argument is the slice
|
|
98
|
+
if err := c.WriteValueExpr(exp.Args[0]); err != nil {
|
|
99
|
+
return fmt.Errorf("failed to write slice in append call: %w", err)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// The remaining arguments are the elements to append
|
|
103
|
+
for i, arg := range exp.Args[1:] {
|
|
104
|
+
if i > 0 || len(exp.Args) > 1 {
|
|
105
|
+
c.tsw.WriteLiterally(", ")
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Special case: append([]byte, string...) should convert string to bytes
|
|
109
|
+
if exp.Ellipsis != token.NoPos && i == 0 { // This is the first element after slice and has ellipsis
|
|
110
|
+
// Check if the slice is []byte and the argument is a string
|
|
111
|
+
sliceType := c.pkg.TypesInfo.TypeOf(exp.Args[0])
|
|
112
|
+
argType := c.pkg.TypesInfo.TypeOf(arg)
|
|
113
|
+
|
|
114
|
+
if sliceType != nil && argType != nil {
|
|
115
|
+
if c.isByteSliceType(sliceType) && c.isStringType(argType) {
|
|
116
|
+
// Convert string to bytes: append([]byte, string...) -> $.append(slice, ...$.stringToBytes(string))
|
|
117
|
+
c.tsw.WriteLiterally("...$.stringToBytes(")
|
|
118
|
+
if err := c.WriteValueExpr(arg); err != nil {
|
|
119
|
+
return fmt.Errorf("failed to write string argument in append call: %w", err)
|
|
120
|
+
}
|
|
121
|
+
c.tsw.WriteLiterally(")")
|
|
122
|
+
continue
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if err := c.WriteValueExpr(arg); err != nil {
|
|
128
|
+
return fmt.Errorf("failed to write argument %d in append call: %w", i+1, err)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
c.tsw.WriteLiterally(")")
|
|
132
|
+
return nil
|
|
133
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"go/ast"
|
|
5
|
+
"go/types"
|
|
6
|
+
|
|
7
|
+
"github.com/pkg/errors"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// isByteSliceType checks if a type is []byte (slice of uint8)
|
|
11
|
+
func (c *GoToTSCompiler) isByteSliceType(t types.Type) bool {
|
|
12
|
+
if sliceType, isSlice := t.Underlying().(*types.Slice); isSlice {
|
|
13
|
+
if basicElem, isBasic := sliceType.Elem().(*types.Basic); isBasic && basicElem.Kind() == types.Uint8 {
|
|
14
|
+
return true
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return false
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// isRuneSliceType checks if a type is []rune (slice of int32)
|
|
21
|
+
func (c *GoToTSCompiler) isRuneSliceType(t types.Type) bool {
|
|
22
|
+
if sliceType, isSlice := t.Underlying().(*types.Slice); isSlice {
|
|
23
|
+
if basicElem, isBasic := sliceType.Elem().(*types.Basic); isBasic && basicElem.Kind() == types.Int32 {
|
|
24
|
+
return true
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return false
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// isStringType checks if a type is string
|
|
31
|
+
func (c *GoToTSCompiler) isStringType(t types.Type) bool {
|
|
32
|
+
if basic, isBasic := t.Underlying().(*types.Basic); isBasic {
|
|
33
|
+
return basic.Kind() == types.String || basic.Kind() == types.UntypedString
|
|
34
|
+
}
|
|
35
|
+
return false
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// writeByteSliceCreation handles the creation of []byte slices with proper Uint8Array handling
|
|
39
|
+
func (c *GoToTSCompiler) writeByteSliceCreation(lengthArg, capacityArg interface{}) error {
|
|
40
|
+
return c.writeSliceCreationForType(lengthArg, capacityArg, true)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// writeSliceCreationForType handles slice creation with special handling for byte slices
|
|
44
|
+
func (c *GoToTSCompiler) writeSliceCreationForType(lengthArg, capacityArg interface{}, isByteSlice bool) error {
|
|
45
|
+
hasCapacity := capacityArg != nil
|
|
46
|
+
|
|
47
|
+
if isByteSlice && !hasCapacity {
|
|
48
|
+
// make([]byte, len) - capacity equals length, use Uint8Array
|
|
49
|
+
c.tsw.WriteLiterally("new Uint8Array(")
|
|
50
|
+
if err := c.writeExprOrDefault(lengthArg, "0"); err != nil {
|
|
51
|
+
return err
|
|
52
|
+
}
|
|
53
|
+
c.tsw.WriteLiterally(")")
|
|
54
|
+
return nil
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Use $.makeSlice for all other cases
|
|
58
|
+
if isByteSlice {
|
|
59
|
+
c.tsw.WriteLiterally("$.makeSlice<number>(")
|
|
60
|
+
} else {
|
|
61
|
+
return errors.New("writeSliceCreationForType called for non-byte slice without element type")
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if err := c.writeExprOrDefault(lengthArg, "0"); err != nil {
|
|
65
|
+
return err
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if hasCapacity {
|
|
69
|
+
c.tsw.WriteLiterally(", ")
|
|
70
|
+
if err := c.writeExprOrDefault(capacityArg, "0"); err != nil {
|
|
71
|
+
return err
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if isByteSlice {
|
|
76
|
+
c.tsw.WriteLiterally(", 'byte')")
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return nil
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// writeGenericSliceCreation handles the creation of generic slices with proper type hints
|
|
83
|
+
func (c *GoToTSCompiler) writeGenericSliceCreation(elemType types.Type, lengthArg, capacityArg interface{}) error {
|
|
84
|
+
hasCapacity := capacityArg != nil
|
|
85
|
+
|
|
86
|
+
c.tsw.WriteLiterally("$.makeSlice<")
|
|
87
|
+
c.WriteGoType(elemType, GoTypeContextGeneral)
|
|
88
|
+
c.tsw.WriteLiterally(">(")
|
|
89
|
+
|
|
90
|
+
if err := c.writeExprOrDefault(lengthArg, "0"); err != nil {
|
|
91
|
+
return err
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if hasCapacity {
|
|
95
|
+
c.tsw.WriteLiterally(", ")
|
|
96
|
+
if err := c.writeExprOrDefault(capacityArg, "0"); err != nil {
|
|
97
|
+
return err
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Add type hint for proper zero value initialization
|
|
102
|
+
c.writeSliceTypeHint(elemType, hasCapacity)
|
|
103
|
+
c.tsw.WriteLiterally(")")
|
|
104
|
+
return nil
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// writeSliceTypeHint writes the type hint parameter for makeSlice calls
|
|
108
|
+
func (c *GoToTSCompiler) writeSliceTypeHint(elemType types.Type, hasCapacity bool) {
|
|
109
|
+
typeHint := c.getTypeHintForSliceElement(elemType)
|
|
110
|
+
if typeHint != "" {
|
|
111
|
+
if !hasCapacity {
|
|
112
|
+
c.tsw.WriteLiterally(", undefined")
|
|
113
|
+
}
|
|
114
|
+
c.tsw.WriteLiterally(", '")
|
|
115
|
+
c.tsw.WriteLiterally(typeHint)
|
|
116
|
+
c.tsw.WriteLiterally("'")
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// writeExprOrDefault writes an expression if it's not nil, otherwise writes a default value
|
|
121
|
+
func (c *GoToTSCompiler) writeExprOrDefault(expr interface{}, defaultValue string) error {
|
|
122
|
+
if expr == nil {
|
|
123
|
+
c.tsw.WriteLiterally(defaultValue)
|
|
124
|
+
return nil
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
switch e := expr.(type) {
|
|
128
|
+
case string:
|
|
129
|
+
c.tsw.WriteLiterally(e)
|
|
130
|
+
return nil
|
|
131
|
+
case ast.Expr:
|
|
132
|
+
// If it's an ast.Expr, call WriteValueExpr directly
|
|
133
|
+
return c.WriteValueExpr(e)
|
|
134
|
+
default:
|
|
135
|
+
// If we can't handle the type, return an error
|
|
136
|
+
return errors.Errorf("unsupported expression type in writeExprOrDefault: %T", e)
|
|
137
|
+
}
|
|
138
|
+
}
|