goscript 0.0.20 → 0.0.22
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/builtin/builtin.ts +390 -119
- package/compiler/assignment.go +407 -0
- package/compiler/compiler.go +203 -5879
- package/compiler/compiler_test.go +4 -0
- package/compiler/composite-lit.go +537 -0
- package/compiler/config.go +3 -0
- package/compiler/decl.go +223 -0
- package/compiler/expr-call.go +423 -0
- package/compiler/expr-selector.go +105 -0
- package/compiler/expr-star.go +90 -0
- package/compiler/expr-type.go +182 -0
- package/compiler/expr-value.go +119 -0
- package/compiler/expr.go +356 -0
- package/compiler/field.go +169 -0
- package/compiler/lit.go +99 -0
- package/compiler/output.go +1 -1
- package/compiler/primitive.go +142 -0
- package/compiler/{write-type-spec.go → spec-struct.go} +79 -203
- package/compiler/spec-value.go +194 -0
- package/compiler/spec.go +263 -0
- package/compiler/stmt-assign.go +411 -0
- package/compiler/stmt-for.go +178 -0
- package/compiler/stmt-range.go +237 -0
- package/compiler/stmt.go +907 -0
- package/compiler/type-assert.go +463 -0
- package/compiler/type-info.go +141 -0
- package/compiler/type.go +556 -0
- package/dist/builtin/builtin.d.ts +24 -6
- package/dist/builtin/builtin.js +215 -19
- package/dist/builtin/builtin.js.map +1 -1
- package/go.mod +2 -1
- package/go.sum +4 -2
- package/package.json +2 -2
- /package/compiler/{writer.go → code-writer.go} +0 -0
package/compiler/decl.go
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"go/ast"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// WriteDecls iterates through a slice of Go top-level declarations (`ast.Decl`)
|
|
9
|
+
// and translates each one into its TypeScript equivalent.
|
|
10
|
+
// It distinguishes between:
|
|
11
|
+
// - Function declarations (`ast.FuncDecl`):
|
|
12
|
+
// - If it's a regular function (no receiver), it delegates to `WriteFuncDeclAsFunction`.
|
|
13
|
+
// - Methods (with receivers) are handled within `WriteTypeSpec` when their
|
|
14
|
+
// associated struct/type is defined, so they are skipped here.
|
|
15
|
+
// - General declarations (`ast.GenDecl`), which can contain imports, constants,
|
|
16
|
+
// variables, or type definitions: It iterates through `d.Specs` and calls
|
|
17
|
+
// `WriteSpec` for each specification.
|
|
18
|
+
//
|
|
19
|
+
// A newline is added after each processed declaration or spec group for readability.
|
|
20
|
+
// Unknown declaration types result in a printed diagnostic message.
|
|
21
|
+
func (c *GoToTSCompiler) WriteDecls(decls []ast.Decl) error {
|
|
22
|
+
for _, decl := range decls {
|
|
23
|
+
switch d := decl.(type) {
|
|
24
|
+
case *ast.FuncDecl:
|
|
25
|
+
// Only handle top-level functions here. Methods are handled within WriteTypeSpec.
|
|
26
|
+
if d.Recv == nil {
|
|
27
|
+
if err := c.WriteFuncDeclAsFunction(d); err != nil {
|
|
28
|
+
return err
|
|
29
|
+
}
|
|
30
|
+
c.tsw.WriteLine("") // Add space after function
|
|
31
|
+
}
|
|
32
|
+
case *ast.GenDecl:
|
|
33
|
+
for _, spec := range d.Specs {
|
|
34
|
+
if err := c.WriteSpec(spec); err != nil {
|
|
35
|
+
return err
|
|
36
|
+
}
|
|
37
|
+
c.tsw.WriteLine("") // Add space after spec
|
|
38
|
+
}
|
|
39
|
+
default:
|
|
40
|
+
fmt.Printf("unknown decl: %#v\n", decl)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return nil
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// WriteFuncDeclAsFunction translates a Go function declaration (`ast.FuncDecl`)
|
|
47
|
+
// that does not have a receiver (i.e., it's a regular function, not a method)
|
|
48
|
+
// into a TypeScript function.
|
|
49
|
+
// - Go documentation comments (`decl.Doc`) are preserved.
|
|
50
|
+
// - If the Go function is exported (name starts with an uppercase letter) or is
|
|
51
|
+
// the `main` function, the `export` keyword is added to the TypeScript output.
|
|
52
|
+
// - If the `Analysis` data indicates the function is asynchronous, the `async`
|
|
53
|
+
// keyword is prepended.
|
|
54
|
+
// - The function signature (parameters and return type) is translated using `WriteFuncType`,
|
|
55
|
+
// passing the `isAsync` status.
|
|
56
|
+
// - The function body (`decl.Body`) is translated using `WriteStmt`.
|
|
57
|
+
//
|
|
58
|
+
// This function specifically handles top-level functions; methods are generated
|
|
59
|
+
// by `WriteFuncDeclAsMethod` within the context of their type definition.
|
|
60
|
+
func (c *GoToTSCompiler) WriteFuncDeclAsFunction(decl *ast.FuncDecl) error {
|
|
61
|
+
if decl.Recv != nil {
|
|
62
|
+
// This function should not be called for methods.
|
|
63
|
+
// Methods are handled by WriteFuncDeclAsMethod within WriteTypeSpec.
|
|
64
|
+
return nil
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if decl.Doc != nil {
|
|
68
|
+
c.WriteDoc(decl.Doc)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Exported functions start with uppercase in Go, or special-case "main" entry point
|
|
72
|
+
isExported := decl.Name.IsExported() || decl.Name.Name == "main"
|
|
73
|
+
if isExported {
|
|
74
|
+
c.tsw.WriteLiterally("export ")
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Check if this function is async using the analysis data
|
|
78
|
+
var isAsync bool
|
|
79
|
+
if obj := c.pkg.TypesInfo.Defs[decl.Name]; obj != nil {
|
|
80
|
+
isAsync = c.analysis.IsAsyncFunc(obj)
|
|
81
|
+
}
|
|
82
|
+
if isAsync {
|
|
83
|
+
c.tsw.WriteLiterally("async ")
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
c.tsw.WriteLiterally("function ")
|
|
87
|
+
if err := c.WriteValueExpr(decl.Name); err != nil { // Function name is a value identifier
|
|
88
|
+
return fmt.Errorf("failed to write function name: %w", err)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// WriteFuncType needs to be aware if the function is async
|
|
92
|
+
c.WriteFuncType(decl.Type, isAsync) // Write signature (params, return type)
|
|
93
|
+
c.tsw.WriteLiterally(" ")
|
|
94
|
+
|
|
95
|
+
if err := c.WriteStmt(decl.Body); err != nil {
|
|
96
|
+
return fmt.Errorf("failed to write function body: %w", err)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return nil
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// WriteFuncDeclAsMethod translates a Go function declaration (`ast.FuncDecl`)
|
|
103
|
+
// that has a receiver (i.e., it's a method) into a TypeScript class method.
|
|
104
|
+
// - It preserves Go documentation comments (`decl.Doc`).
|
|
105
|
+
// - The method is declared as `public`.
|
|
106
|
+
// - If the `Analysis` data indicates the method is asynchronous, the `async`
|
|
107
|
+
// keyword is prepended.
|
|
108
|
+
// - The method name retains its original Go casing.
|
|
109
|
+
// - Parameters and return types are translated using `WriteFieldList` and
|
|
110
|
+
// `WriteTypeExpr`, respectively. Async methods have their return types
|
|
111
|
+
// wrapped in `Promise<>`.
|
|
112
|
+
// - The method body is translated. If the Go receiver has a name (e.g., `(s *MyStruct)`),
|
|
113
|
+
// a `const receiverName = this;` binding is generated at the start of the
|
|
114
|
+
// TypeScript method body to make `this` available via the Go receiver's name.
|
|
115
|
+
// If the method body requires deferred cleanup (`NeedsDefer`), the appropriate
|
|
116
|
+
// `using __defer = new $.DisposableStack()` (or `AsyncDisposableStack`)
|
|
117
|
+
// is also generated.
|
|
118
|
+
//
|
|
119
|
+
// This function assumes it is called only for `FuncDecl` nodes that are methods.
|
|
120
|
+
func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
|
|
121
|
+
if decl.Doc != nil {
|
|
122
|
+
c.WriteDoc(decl.Doc)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Determine if method is async
|
|
126
|
+
var isAsync bool
|
|
127
|
+
if obj := c.pkg.TypesInfo.Defs[decl.Name]; obj != nil {
|
|
128
|
+
isAsync = c.analysis.IsAsyncFunc(obj)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Methods are typically public in the TS output
|
|
132
|
+
c.tsw.WriteLiterally("public ")
|
|
133
|
+
|
|
134
|
+
// Add async modifier if needed
|
|
135
|
+
if isAsync {
|
|
136
|
+
c.tsw.WriteLiterally("async ")
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Keep original Go casing for method names
|
|
140
|
+
if err := c.WriteValueExpr(decl.Name); err != nil { // Method name is a value identifier
|
|
141
|
+
return err
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Write signature (parameters and return type)
|
|
145
|
+
// We adapt the logic from WriteFuncType here, but without the 'function' keyword
|
|
146
|
+
funcType := decl.Type
|
|
147
|
+
c.tsw.WriteLiterally("(")
|
|
148
|
+
if funcType.Params != nil {
|
|
149
|
+
c.WriteFieldList(funcType.Params, true) // true = arguments
|
|
150
|
+
}
|
|
151
|
+
c.tsw.WriteLiterally(")")
|
|
152
|
+
|
|
153
|
+
// Handle return type
|
|
154
|
+
if funcType.Results != nil && len(funcType.Results.List) > 0 {
|
|
155
|
+
c.tsw.WriteLiterally(": ")
|
|
156
|
+
if isAsync {
|
|
157
|
+
c.tsw.WriteLiterally("Promise<")
|
|
158
|
+
}
|
|
159
|
+
if len(funcType.Results.List) == 1 {
|
|
160
|
+
// Single return value
|
|
161
|
+
resultType := funcType.Results.List[0].Type
|
|
162
|
+
c.WriteTypeExpr(resultType)
|
|
163
|
+
} else {
|
|
164
|
+
// Multiple return values -> tuple type
|
|
165
|
+
c.tsw.WriteLiterally("[")
|
|
166
|
+
for i, field := range funcType.Results.List {
|
|
167
|
+
if i > 0 {
|
|
168
|
+
c.tsw.WriteLiterally(", ")
|
|
169
|
+
}
|
|
170
|
+
c.WriteTypeExpr(field.Type)
|
|
171
|
+
}
|
|
172
|
+
c.tsw.WriteLiterally("]")
|
|
173
|
+
}
|
|
174
|
+
if isAsync {
|
|
175
|
+
c.tsw.WriteLiterally(">")
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
// No return value -> void
|
|
179
|
+
if isAsync {
|
|
180
|
+
c.tsw.WriteLiterally(": Promise<void>")
|
|
181
|
+
} else {
|
|
182
|
+
c.tsw.WriteLiterally(": void")
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
c.tsw.WriteLiterally(" ")
|
|
187
|
+
|
|
188
|
+
// Bind receiver name to this
|
|
189
|
+
if recvField := decl.Recv.List[0]; len(recvField.Names) > 0 {
|
|
190
|
+
recvName := recvField.Names[0].Name
|
|
191
|
+
if recvName != "_" {
|
|
192
|
+
c.tsw.WriteLine("{")
|
|
193
|
+
c.tsw.Indent(1)
|
|
194
|
+
c.tsw.WriteLinef("const %s = this", recvName)
|
|
195
|
+
|
|
196
|
+
// Add using statement if needed
|
|
197
|
+
if c.analysis.NeedsDefer(decl.Body) {
|
|
198
|
+
if c.analysis.IsInAsyncFunction(decl) {
|
|
199
|
+
c.tsw.WriteLine("await using __defer = new $.AsyncDisposableStack();")
|
|
200
|
+
} else {
|
|
201
|
+
c.tsw.WriteLine("using cleanup = new $.DisposableStack();")
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// write method body without outer braces
|
|
206
|
+
for _, stmt := range decl.Body.List {
|
|
207
|
+
if err := c.WriteStmt(stmt); err != nil {
|
|
208
|
+
return fmt.Errorf("failed to write statement in function body: %w", err)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
c.tsw.Indent(-1)
|
|
212
|
+
c.tsw.WriteLine("}")
|
|
213
|
+
|
|
214
|
+
return nil
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// no named receiver, write whole body
|
|
218
|
+
if err := c.WriteStmt(decl.Body); err != nil {
|
|
219
|
+
return fmt.Errorf("failed to write function body: %w", err)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return nil
|
|
223
|
+
}
|
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"go/ast"
|
|
6
|
+
"go/token"
|
|
7
|
+
"go/types"
|
|
8
|
+
|
|
9
|
+
"github.com/pkg/errors"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
// WriteCallExpr translates a Go function call expression (`ast.CallExpr`)
|
|
13
|
+
// into its TypeScript equivalent.
|
|
14
|
+
// It handles several Go built-in functions specially:
|
|
15
|
+
// - `println(...)` becomes `console.log(...)`.
|
|
16
|
+
// - `len(arg)` becomes `$.len(arg)`.
|
|
17
|
+
// - `cap(arg)` becomes `$.cap(arg)`.
|
|
18
|
+
// - `delete(m, k)` becomes `$.deleteMapEntry(m, k)`.
|
|
19
|
+
// - `make(chan T, size)` becomes `$.makeChannel<T_ts>(size, zeroValueForT)`.
|
|
20
|
+
// - `make(map[K]V)` becomes `$.makeMap<K_ts, V_ts>()`.
|
|
21
|
+
// - `make([]T, len, cap)` becomes `$.makeSlice<T_ts>(len, cap)`.
|
|
22
|
+
// - `string(runeVal)` becomes `String.fromCharCode(runeVal)`.
|
|
23
|
+
// - `string([]runeVal)` or `string([]byteVal)` becomes `$.runesToString(sliceVal)`.
|
|
24
|
+
// - `[]rune(stringVal)` becomes `$.stringToRunes(stringVal)`.
|
|
25
|
+
// - `close(ch)` becomes `ch.close()`.
|
|
26
|
+
// - `append(slice, elems...)` becomes `$.append(slice, elems...)`.
|
|
27
|
+
// - `byte(val)` becomes `$.byte(val)`.
|
|
28
|
+
// For other function calls:
|
|
29
|
+
// - If the `Analysis` data indicates the function is asynchronous (e.g., due to
|
|
30
|
+
// channel operations or `go`/`defer` usage within it), the call is prefixed with `await`.
|
|
31
|
+
// - Otherwise, it's translated as a standard TypeScript function call: `funcName(arg1, arg2)`.
|
|
32
|
+
//
|
|
33
|
+
// Arguments are recursively translated using `WriteValueExpr`.
|
|
34
|
+
func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
35
|
+
expFun := exp.Fun
|
|
36
|
+
|
|
37
|
+
// Handle any type conversion with nil argument
|
|
38
|
+
if len(exp.Args) == 1 {
|
|
39
|
+
if nilIdent, isIdent := exp.Args[0].(*ast.Ident); isIdent && nilIdent.Name == "nil" {
|
|
40
|
+
// Handle nil pointer to struct type conversions: (*struct{})(nil)
|
|
41
|
+
if starExpr, isStarExpr := expFun.(*ast.StarExpr); isStarExpr {
|
|
42
|
+
if _, isStructType := starExpr.X.(*ast.StructType); isStructType {
|
|
43
|
+
c.tsw.WriteLiterally("null")
|
|
44
|
+
return nil
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
c.tsw.WriteLiterally("null")
|
|
49
|
+
return nil
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Handle array type conversions like []rune(string)
|
|
54
|
+
if arrayType, isArrayType := expFun.(*ast.ArrayType); isArrayType {
|
|
55
|
+
// Check if it's a []rune type
|
|
56
|
+
if ident, isIdent := arrayType.Elt.(*ast.Ident); isIdent && ident.Name == "rune" {
|
|
57
|
+
// Check if the argument is a string
|
|
58
|
+
if len(exp.Args) == 1 {
|
|
59
|
+
arg := exp.Args[0]
|
|
60
|
+
if tv, ok := c.pkg.TypesInfo.Types[arg]; ok && tv.Type != nil {
|
|
61
|
+
if basic, isBasic := tv.Type.Underlying().(*types.Basic); isBasic && basic.Kind() == types.String {
|
|
62
|
+
// Translate []rune(stringValue) to $.stringToRunes(stringValue)
|
|
63
|
+
c.tsw.WriteLiterally("$.stringToRunes(")
|
|
64
|
+
if err := c.WriteValueExpr(arg); err != nil {
|
|
65
|
+
return fmt.Errorf("failed to write argument for []rune(string) conversion: %w", err)
|
|
66
|
+
}
|
|
67
|
+
c.tsw.WriteLiterally(")")
|
|
68
|
+
return nil // Handled []rune(string)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if funIdent, funIsIdent := expFun.(*ast.Ident); funIsIdent {
|
|
76
|
+
switch funIdent.String() {
|
|
77
|
+
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
|
|
89
|
+
case "len":
|
|
90
|
+
// Translate len(arg) to $.len(arg)
|
|
91
|
+
if len(exp.Args) == 1 {
|
|
92
|
+
c.tsw.WriteLiterally("$.len(")
|
|
93
|
+
if err := c.WriteValueExpr(exp.Args[0]); err != nil {
|
|
94
|
+
return err
|
|
95
|
+
}
|
|
96
|
+
c.tsw.WriteLiterally(")")
|
|
97
|
+
return nil // Handled len
|
|
98
|
+
}
|
|
99
|
+
return errors.New("unhandled len call with incorrect number of arguments")
|
|
100
|
+
case "cap":
|
|
101
|
+
// Translate cap(arg) to $.cap(arg)
|
|
102
|
+
if len(exp.Args) == 1 {
|
|
103
|
+
c.tsw.WriteLiterally("$.cap(")
|
|
104
|
+
if err := c.WriteValueExpr(exp.Args[0]); err != nil {
|
|
105
|
+
return err
|
|
106
|
+
}
|
|
107
|
+
c.tsw.WriteLiterally(")")
|
|
108
|
+
return nil // Handled cap
|
|
109
|
+
}
|
|
110
|
+
return errors.New("unhandled cap call with incorrect number of arguments")
|
|
111
|
+
case "delete":
|
|
112
|
+
// Translate delete(map, key) to $.deleteMapEntry(map, key)
|
|
113
|
+
if len(exp.Args) == 2 {
|
|
114
|
+
c.tsw.WriteLiterally("$.deleteMapEntry(")
|
|
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
|
|
124
|
+
}
|
|
125
|
+
return errors.New("unhandled delete call with incorrect number of arguments")
|
|
126
|
+
case "make":
|
|
127
|
+
// First check if we have a channel type
|
|
128
|
+
if typ := c.pkg.TypesInfo.TypeOf(exp.Args[0]); typ != nil {
|
|
129
|
+
if chanType, ok := typ.Underlying().(*types.Chan); ok {
|
|
130
|
+
// Handle channel creation: make(chan T, bufferSize) or make(chan T)
|
|
131
|
+
c.tsw.WriteLiterally("$.makeChannel<")
|
|
132
|
+
c.WriteGoType(chanType.Elem())
|
|
133
|
+
c.tsw.WriteLiterally(">(")
|
|
134
|
+
|
|
135
|
+
// If buffer size is provided, add it
|
|
136
|
+
if len(exp.Args) >= 2 {
|
|
137
|
+
if err := c.WriteValueExpr(exp.Args[1]); err != nil {
|
|
138
|
+
return fmt.Errorf("failed to write buffer size in makeChannel: %w", err)
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
// Default to 0 (unbuffered channel)
|
|
142
|
+
c.tsw.WriteLiterally("0")
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
c.tsw.WriteLiterally(", ") // Add comma for zero value argument
|
|
146
|
+
|
|
147
|
+
// Write the zero value for the channel's element type
|
|
148
|
+
if chanType.Elem().String() == "struct{}" {
|
|
149
|
+
c.tsw.WriteLiterally("{}")
|
|
150
|
+
} else {
|
|
151
|
+
c.WriteZeroValueForType(chanType.Elem())
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Add direction parameter
|
|
155
|
+
c.tsw.WriteLiterally(", ")
|
|
156
|
+
|
|
157
|
+
// Determine channel direction
|
|
158
|
+
switch chanType.Dir() {
|
|
159
|
+
case types.SendRecv:
|
|
160
|
+
c.tsw.WriteLiterally("'both'")
|
|
161
|
+
case types.SendOnly:
|
|
162
|
+
c.tsw.WriteLiterally("'send'")
|
|
163
|
+
case types.RecvOnly:
|
|
164
|
+
c.tsw.WriteLiterally("'receive'")
|
|
165
|
+
default:
|
|
166
|
+
c.tsw.WriteLiterally("'both'") // Default to bidirectional
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
c.tsw.WriteLiterally(")")
|
|
170
|
+
return nil // Handled make for channel
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// Handle make for slices: make([]T, len, cap) or make([]T, len)
|
|
174
|
+
if len(exp.Args) >= 1 {
|
|
175
|
+
// Handle map creation: make(map[K]V)
|
|
176
|
+
if mapType, ok := exp.Args[0].(*ast.MapType); ok {
|
|
177
|
+
c.tsw.WriteLiterally("$.makeMap<")
|
|
178
|
+
c.WriteTypeExpr(mapType.Key) // Write the key type
|
|
179
|
+
c.tsw.WriteLiterally(", ")
|
|
180
|
+
c.WriteTypeExpr(mapType.Value) // Write the value type
|
|
181
|
+
c.tsw.WriteLiterally(">()")
|
|
182
|
+
return nil // Handled make for map
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Handle slice creation
|
|
186
|
+
if _, ok := exp.Args[0].(*ast.ArrayType); ok {
|
|
187
|
+
// Get the slice type information
|
|
188
|
+
sliceType := c.pkg.TypesInfo.TypeOf(exp.Args[0])
|
|
189
|
+
if sliceType == nil {
|
|
190
|
+
return errors.New("could not get type information for slice in make call")
|
|
191
|
+
}
|
|
192
|
+
goElemType, ok := sliceType.Underlying().(*types.Slice)
|
|
193
|
+
if !ok {
|
|
194
|
+
return errors.New("expected slice type for make call")
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
c.tsw.WriteLiterally("$.makeSlice<")
|
|
198
|
+
c.WriteGoType(goElemType.Elem()) // Write the element type
|
|
199
|
+
c.tsw.WriteLiterally(">(")
|
|
200
|
+
|
|
201
|
+
if len(exp.Args) >= 2 {
|
|
202
|
+
if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
|
|
203
|
+
return err
|
|
204
|
+
}
|
|
205
|
+
if len(exp.Args) == 3 {
|
|
206
|
+
c.tsw.WriteLiterally(", ")
|
|
207
|
+
if err := c.WriteValueExpr(exp.Args[2]); err != nil { // Capacity
|
|
208
|
+
return err
|
|
209
|
+
}
|
|
210
|
+
} else if len(exp.Args) > 3 {
|
|
211
|
+
return errors.New("makeSlice expects 2 or 3 arguments")
|
|
212
|
+
}
|
|
213
|
+
} else {
|
|
214
|
+
// If no length is provided, default to 0
|
|
215
|
+
c.tsw.WriteLiterally("0")
|
|
216
|
+
}
|
|
217
|
+
c.tsw.WriteLiterally(")")
|
|
218
|
+
return nil // Handled make for slice
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// Fallthrough for unhandled make calls (e.g., channels)
|
|
222
|
+
return errors.New("unhandled make call")
|
|
223
|
+
case "string":
|
|
224
|
+
// Handle string() conversion
|
|
225
|
+
if len(exp.Args) == 1 {
|
|
226
|
+
arg := exp.Args[0]
|
|
227
|
+
|
|
228
|
+
// Case 1: Argument is a string literal string("...")
|
|
229
|
+
if basicLit, isBasicLit := arg.(*ast.BasicLit); isBasicLit && basicLit.Kind == token.STRING {
|
|
230
|
+
// Translate string("...") to "..." (no-op)
|
|
231
|
+
c.WriteBasicLit(basicLit)
|
|
232
|
+
return nil // Handled string literal conversion
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Case 2: Argument is a rune (int32) or a call to rune()
|
|
236
|
+
innerCall, isCallExpr := arg.(*ast.CallExpr)
|
|
237
|
+
|
|
238
|
+
if isCallExpr {
|
|
239
|
+
// Check if it's a call to rune()
|
|
240
|
+
if innerFunIdent, innerFunIsIdent := innerCall.Fun.(*ast.Ident); innerFunIsIdent && innerFunIdent.String() == "rune" {
|
|
241
|
+
// Translate string(rune(val)) to String.fromCharCode(val)
|
|
242
|
+
if len(innerCall.Args) == 1 {
|
|
243
|
+
c.tsw.WriteLiterally("String.fromCharCode(")
|
|
244
|
+
if err := c.WriteValueExpr(innerCall.Args[0]); err != nil {
|
|
245
|
+
return fmt.Errorf("failed to write argument for string(rune) conversion: %w", err)
|
|
246
|
+
}
|
|
247
|
+
c.tsw.WriteLiterally(")")
|
|
248
|
+
return nil // Handled string(rune)
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Handle direct string(int32) conversion
|
|
254
|
+
// This assumes 'rune' is int32
|
|
255
|
+
if tv, ok := c.pkg.TypesInfo.Types[arg]; ok {
|
|
256
|
+
if basic, isBasic := tv.Type.Underlying().(*types.Basic); isBasic && basic.Kind() == types.Int32 {
|
|
257
|
+
// Translate string(rune_val) to String.fromCharCode(rune_val)
|
|
258
|
+
c.tsw.WriteLiterally("String.fromCharCode(")
|
|
259
|
+
if err := c.WriteValueExpr(arg); err != nil {
|
|
260
|
+
return fmt.Errorf("failed to write argument for string(int32) conversion: %w", err)
|
|
261
|
+
}
|
|
262
|
+
c.tsw.WriteLiterally(")")
|
|
263
|
+
return nil // Handled string(int32)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Case 3: Argument is a slice of runes or bytes string([]rune{...}) or string([]byte{...})
|
|
267
|
+
if sliceType, isSlice := tv.Type.Underlying().(*types.Slice); isSlice {
|
|
268
|
+
if basic, isBasic := sliceType.Elem().Underlying().(*types.Basic); isBasic {
|
|
269
|
+
// Handle both runes (int32) and bytes (uint8)
|
|
270
|
+
if basic.Kind() == types.Int32 || basic.Kind() == types.Uint8 {
|
|
271
|
+
// Translate string([]rune) or string([]byte) to $.runesToString(...)
|
|
272
|
+
c.tsw.WriteLiterally("$.runesToString(")
|
|
273
|
+
if err := c.WriteValueExpr(arg); err != nil {
|
|
274
|
+
return fmt.Errorf("failed to write argument for string([]rune/[]byte) conversion: %w", err)
|
|
275
|
+
}
|
|
276
|
+
c.tsw.WriteLiterally(")")
|
|
277
|
+
return nil // Handled string([]rune) or string([]byte)
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
// Return error for other unhandled string conversions
|
|
284
|
+
return fmt.Errorf("unhandled string conversion: %s", exp.Fun)
|
|
285
|
+
case "close":
|
|
286
|
+
// Translate close(ch) to ch.close()
|
|
287
|
+
if len(exp.Args) == 1 {
|
|
288
|
+
if err := c.WriteValueExpr(exp.Args[0]); err != nil {
|
|
289
|
+
return fmt.Errorf("failed to write channel in close call: %w", err)
|
|
290
|
+
}
|
|
291
|
+
c.tsw.WriteLiterally(".close()")
|
|
292
|
+
return nil // Handled close
|
|
293
|
+
}
|
|
294
|
+
return errors.New("unhandled close call with incorrect number of arguments")
|
|
295
|
+
case "append":
|
|
296
|
+
// Translate append(slice, elements...) to $.append(slice, elements...)
|
|
297
|
+
if len(exp.Args) >= 1 {
|
|
298
|
+
c.tsw.WriteLiterally("$.append(")
|
|
299
|
+
// The first argument is the slice
|
|
300
|
+
if err := c.WriteValueExpr(exp.Args[0]); err != nil {
|
|
301
|
+
return fmt.Errorf("failed to write slice in append call: %w", err)
|
|
302
|
+
}
|
|
303
|
+
// The remaining arguments are the elements to append
|
|
304
|
+
for i, arg := range exp.Args[1:] {
|
|
305
|
+
if i > 0 || len(exp.Args) > 1 { // Add comma before elements if there are any
|
|
306
|
+
c.tsw.WriteLiterally(", ")
|
|
307
|
+
}
|
|
308
|
+
if err := c.WriteValueExpr(arg); err != nil {
|
|
309
|
+
return fmt.Errorf("failed to write argument %d in append call: %w", i+1, err)
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
c.tsw.WriteLiterally(")")
|
|
313
|
+
return nil // Handled append
|
|
314
|
+
}
|
|
315
|
+
return errors.New("unhandled append call with incorrect number of arguments")
|
|
316
|
+
case "byte":
|
|
317
|
+
// Translate byte(val) to $.byte(val)
|
|
318
|
+
if len(exp.Args) == 1 {
|
|
319
|
+
c.tsw.WriteLiterally("$.byte(")
|
|
320
|
+
if err := c.WriteValueExpr(exp.Args[0]); err != nil {
|
|
321
|
+
return err
|
|
322
|
+
}
|
|
323
|
+
c.tsw.WriteLiterally(")")
|
|
324
|
+
return nil // Handled byte
|
|
325
|
+
}
|
|
326
|
+
return errors.New("unhandled byte call with incorrect number of arguments")
|
|
327
|
+
default:
|
|
328
|
+
// Check if this is a type conversion to a function type
|
|
329
|
+
if funIdent != nil {
|
|
330
|
+
if obj := c.pkg.TypesInfo.Uses[funIdent]; obj != nil {
|
|
331
|
+
// Check if the object is a type name
|
|
332
|
+
if typeName, isType := obj.(*types.TypeName); isType {
|
|
333
|
+
// Make sure we have exactly one argument
|
|
334
|
+
if len(exp.Args) == 1 {
|
|
335
|
+
// Check if this is a function type
|
|
336
|
+
if _, isFuncType := typeName.Type().Underlying().(*types.Signature); isFuncType {
|
|
337
|
+
// For function types, we need to add a __goTypeName property
|
|
338
|
+
c.tsw.WriteLiterally("Object.assign(")
|
|
339
|
+
|
|
340
|
+
// Write the argument first
|
|
341
|
+
if err := c.WriteValueExpr(exp.Args[0]); err != nil {
|
|
342
|
+
return fmt.Errorf("failed to write argument for function type cast: %w", err)
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Add the __goTypeName property with the function type name
|
|
346
|
+
c.tsw.WriteLiterallyf(", { __goTypeName: '%s' })", funIdent.String())
|
|
347
|
+
return nil // Handled function type cast
|
|
348
|
+
} else {
|
|
349
|
+
// For non-function types, use the TypeScript "as" operator
|
|
350
|
+
c.tsw.WriteLiterally("(")
|
|
351
|
+
if err := c.WriteValueExpr(exp.Args[0]); err != nil {
|
|
352
|
+
return fmt.Errorf("failed to write argument for type cast: %w", err)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Then use the TypeScript "as" operator with the type name
|
|
356
|
+
c.tsw.WriteLiterallyf(" as %s)", funIdent.String())
|
|
357
|
+
return nil // Handled non-function type cast
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Check if this is an async function call
|
|
365
|
+
if funIdent != nil {
|
|
366
|
+
// Get the object for this function identifier
|
|
367
|
+
if obj := c.pkg.TypesInfo.Uses[funIdent]; obj != nil && c.analysis.IsAsyncFunc(obj) {
|
|
368
|
+
// This is an async function
|
|
369
|
+
c.tsw.WriteLiterally("await ")
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Not a special built-in, treat as a regular function call
|
|
374
|
+
if err := c.WriteValueExpr(expFun); err != nil {
|
|
375
|
+
return fmt.Errorf("failed to write function expression in call: %w", err)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if funType := c.pkg.TypesInfo.TypeOf(expFun); funType != nil {
|
|
379
|
+
if _, ok := funType.Underlying().(*types.Signature); ok {
|
|
380
|
+
if _, isNamed := funType.(*types.Named); isNamed {
|
|
381
|
+
c.tsw.WriteLiterally("!")
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
c.tsw.WriteLiterally("(")
|
|
387
|
+
for i, arg := range exp.Args {
|
|
388
|
+
if i != 0 {
|
|
389
|
+
c.tsw.WriteLiterally(", ")
|
|
390
|
+
}
|
|
391
|
+
if err := c.WriteValueExpr(arg); err != nil {
|
|
392
|
+
return fmt.Errorf("failed to write argument %d in call: %w", i, err)
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
c.tsw.WriteLiterally(")")
|
|
396
|
+
return nil // Handled regular function call
|
|
397
|
+
}
|
|
398
|
+
} else {
|
|
399
|
+
// Not an identifier (e.g., method call on a value)
|
|
400
|
+
if err := c.WriteValueExpr(expFun); err != nil {
|
|
401
|
+
return fmt.Errorf("failed to write method expression in call: %w", err)
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if funType := c.pkg.TypesInfo.TypeOf(expFun); funType != nil {
|
|
405
|
+
if _, ok := funType.Underlying().(*types.Signature); ok {
|
|
406
|
+
if _, isNamed := funType.(*types.Named); isNamed {
|
|
407
|
+
c.tsw.WriteLiterally("!")
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
c.tsw.WriteLiterally("(")
|
|
413
|
+
for i, arg := range exp.Args {
|
|
414
|
+
if i != 0 {
|
|
415
|
+
c.tsw.WriteLiterally(", ")
|
|
416
|
+
}
|
|
417
|
+
if err := c.WriteValueExpr(arg); err != nil {
|
|
418
|
+
return fmt.Errorf("failed to write argument %d in call: %w", i, err)
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
c.tsw.WriteLiterally(")")
|
|
422
|
+
return nil
|
|
423
|
+
}
|