goscript 0.0.57 → 0.0.59
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 +40 -33
- package/compiler/analysis.go +115 -19
- package/compiler/assignment.go +163 -217
- package/compiler/compiler.go +35 -31
- package/compiler/composite-lit.go +233 -196
- package/compiler/constraint.go +88 -0
- package/compiler/decl.go +418 -7
- package/compiler/expr-call-async.go +20 -34
- package/compiler/expr-call-builtins.go +19 -0
- package/compiler/expr-call-helpers.go +0 -28
- package/compiler/expr-call-make.go +93 -343
- package/compiler/expr-call-type-conversion.go +221 -249
- package/compiler/expr-call.go +70 -69
- package/compiler/expr-selector.go +21 -24
- package/compiler/expr.go +3 -60
- package/compiler/protobuf.go +180 -36
- package/compiler/spec-value.go +132 -24
- package/compiler/spec.go +14 -55
- package/compiler/stmt-assign.go +338 -356
- package/compiler/stmt-range.go +4 -24
- package/compiler/stmt.go +99 -172
- package/compiler/type-utils.go +185 -0
- package/compiler/type.go +26 -80
- package/dist/gs/builtin/slice.d.ts +1 -1
- package/dist/gs/builtin/slice.js +3 -0
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/builtin/type.js +8 -2
- package/dist/gs/builtin/type.js.map +1 -1
- package/dist/gs/fmt/fmt.js +113 -16
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/runtime/runtime.d.ts +1 -1
- package/dist/gs/runtime/runtime.js +1 -1
- package/dist/gs/slices/slices.d.ts +23 -0
- package/dist/gs/slices/slices.js +61 -0
- package/dist/gs/slices/slices.js.map +1 -1
- package/go.mod +8 -8
- package/go.sum +14 -14
- package/gs/builtin/slice.ts +5 -2
- package/gs/builtin/type.ts +13 -6
- package/gs/fmt/fmt.test.ts +176 -0
- package/gs/fmt/fmt.ts +109 -18
- package/gs/runtime/runtime.ts +1 -1
- package/gs/slices/slices.ts +68 -0
- package/package.json +3 -3
package/compiler/decl.go
CHANGED
|
@@ -3,7 +3,9 @@ package compiler
|
|
|
3
3
|
import (
|
|
4
4
|
"fmt"
|
|
5
5
|
"go/ast"
|
|
6
|
+
"go/token"
|
|
6
7
|
"go/types"
|
|
8
|
+
"sort"
|
|
7
9
|
)
|
|
8
10
|
|
|
9
11
|
// WriteDecls iterates through a slice of Go top-level declarations (`ast.Decl`)
|
|
@@ -17,33 +19,442 @@ import (
|
|
|
17
19
|
// variables, or type definitions: It iterates through `d.Specs` and calls
|
|
18
20
|
// `WriteSpec` for each specification.
|
|
19
21
|
//
|
|
22
|
+
// Type declarations are sorted by dependencies to ensure referenced types are
|
|
23
|
+
// defined before types that reference them, avoiding initialization order issues.
|
|
20
24
|
// A newline is added after each processed declaration or spec group for readability.
|
|
21
25
|
// Unknown declaration types result in a printed diagnostic message.
|
|
22
26
|
func (c *GoToTSCompiler) WriteDecls(decls []ast.Decl) error {
|
|
27
|
+
// Separate type declarations from other declarations for dependency sorting
|
|
28
|
+
var typeSpecs []*ast.TypeSpec
|
|
29
|
+
var varSpecs []*ast.ValueSpec
|
|
30
|
+
var otherDecls []ast.Decl
|
|
31
|
+
var otherSpecs []ast.Spec
|
|
32
|
+
|
|
23
33
|
for _, decl := range decls {
|
|
24
34
|
switch d := decl.(type) {
|
|
25
35
|
case *ast.FuncDecl:
|
|
26
36
|
// Only handle top-level functions here. Methods are handled within WriteTypeSpec.
|
|
27
37
|
if d.Recv == nil {
|
|
28
|
-
|
|
29
|
-
return err
|
|
30
|
-
}
|
|
31
|
-
c.tsw.WriteLine("") // Add space after function
|
|
38
|
+
otherDecls = append(otherDecls, d)
|
|
32
39
|
}
|
|
33
40
|
case *ast.GenDecl:
|
|
34
41
|
for _, spec := range d.Specs {
|
|
35
|
-
if
|
|
36
|
-
|
|
42
|
+
if typeSpec, ok := spec.(*ast.TypeSpec); ok {
|
|
43
|
+
typeSpecs = append(typeSpecs, typeSpec)
|
|
44
|
+
} else if varSpec, ok := spec.(*ast.ValueSpec); ok && d.Tok == token.VAR {
|
|
45
|
+
varSpecs = append(varSpecs, varSpec)
|
|
46
|
+
} else {
|
|
47
|
+
otherSpecs = append(otherSpecs, spec)
|
|
37
48
|
}
|
|
38
|
-
c.tsw.WriteLine("") // Add space after spec
|
|
39
49
|
}
|
|
50
|
+
default:
|
|
51
|
+
otherDecls = append(otherDecls, d)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Sort type declarations by dependencies
|
|
56
|
+
sortedTypeSpecs, err := c.sortTypeSpecsByDependencies(typeSpecs)
|
|
57
|
+
if err != nil {
|
|
58
|
+
// Surface the error instead of silently falling back
|
|
59
|
+
return fmt.Errorf("circular dependency detected sorting type declarations: %w", err)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Sort variable declarations by type dependencies
|
|
63
|
+
sortedVarSpecs, err := c.sortVarSpecsByTypeDependencies(varSpecs, typeSpecs)
|
|
64
|
+
if err != nil {
|
|
65
|
+
return fmt.Errorf("failed to sort variable declarations: %w", err)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Write non-type, non-var declarations first (imports, constants)
|
|
69
|
+
for _, spec := range otherSpecs {
|
|
70
|
+
if err := c.WriteSpec(spec); err != nil {
|
|
71
|
+
return err
|
|
72
|
+
}
|
|
73
|
+
c.tsw.WriteLine("") // Add space after spec
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Write sorted type declarations
|
|
77
|
+
for _, typeSpec := range sortedTypeSpecs {
|
|
78
|
+
if err := c.WriteSpec(typeSpec); err != nil {
|
|
79
|
+
return err
|
|
80
|
+
}
|
|
81
|
+
c.tsw.WriteLine("") // Add space after spec
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Write sorted variable declarations
|
|
85
|
+
for _, varSpec := range sortedVarSpecs {
|
|
86
|
+
if err := c.WriteSpec(varSpec); err != nil {
|
|
87
|
+
return err
|
|
88
|
+
}
|
|
89
|
+
c.tsw.WriteLine("") // Add space after spec
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Write function declarations last
|
|
93
|
+
for _, decl := range otherDecls {
|
|
94
|
+
switch d := decl.(type) {
|
|
95
|
+
case *ast.FuncDecl:
|
|
96
|
+
if err := c.WriteFuncDeclAsFunction(d); err != nil {
|
|
97
|
+
return err
|
|
98
|
+
}
|
|
99
|
+
c.tsw.WriteLine("") // Add space after function
|
|
40
100
|
default:
|
|
41
101
|
return fmt.Errorf("unknown decl: %#v", decl)
|
|
42
102
|
}
|
|
43
103
|
}
|
|
104
|
+
|
|
44
105
|
return nil
|
|
45
106
|
}
|
|
46
107
|
|
|
108
|
+
// sortTypeSpecsByDependencies performs a topological sort of type specifications
|
|
109
|
+
// based on their dependencies to ensure referenced types are defined before
|
|
110
|
+
// types that reference them.
|
|
111
|
+
func (c *GoToTSCompiler) sortTypeSpecsByDependencies(typeSpecs []*ast.TypeSpec) ([]*ast.TypeSpec, error) {
|
|
112
|
+
if len(typeSpecs) <= 1 {
|
|
113
|
+
return typeSpecs, nil
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Build dependency graph
|
|
117
|
+
dependencies := make(map[string][]string) // typeName -> list of types it depends on
|
|
118
|
+
typeSpecMap := make(map[string]*ast.TypeSpec)
|
|
119
|
+
|
|
120
|
+
// First pass: collect all type names
|
|
121
|
+
for _, typeSpec := range typeSpecs {
|
|
122
|
+
typeName := typeSpec.Name.Name
|
|
123
|
+
typeSpecMap[typeName] = typeSpec
|
|
124
|
+
dependencies[typeName] = []string{}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Second pass: analyze dependencies
|
|
128
|
+
for _, typeSpec := range typeSpecs {
|
|
129
|
+
typeName := typeSpec.Name.Name
|
|
130
|
+
deps := c.extractTypeDependencies(typeSpec.Type, typeSpecMap)
|
|
131
|
+
dependencies[typeName] = deps
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Perform topological sort
|
|
135
|
+
sorted, err := c.topologicalSort(dependencies)
|
|
136
|
+
if err != nil {
|
|
137
|
+
return nil, err
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Build result in sorted order
|
|
141
|
+
var result []*ast.TypeSpec
|
|
142
|
+
for _, typeName := range sorted {
|
|
143
|
+
if typeSpec, exists := typeSpecMap[typeName]; exists {
|
|
144
|
+
result = append(result, typeSpec)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return result, nil
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// extractTypeDependencies extracts only the dependencies that cause TypeScript initialization order issues.
|
|
152
|
+
// These are dependencies where the constructor of one type directly instantiates another type.
|
|
153
|
+
//
|
|
154
|
+
// TRUE dependencies (cause initialization issues):
|
|
155
|
+
// - Direct struct fields (non-pointer): type A struct { b B } -> A constructor calls new B()
|
|
156
|
+
// - Embedded struct fields: type A struct { B } -> A constructor calls new B()
|
|
157
|
+
// - Direct type aliases: type A B -> A directly wraps B
|
|
158
|
+
// - Array/slice of structs: type A []B -> needs B for default values
|
|
159
|
+
//
|
|
160
|
+
// FALSE dependencies (don't cause initialization issues):
|
|
161
|
+
// - Pointer fields: type A struct { b *B } -> just stores reference, no constructor call
|
|
162
|
+
// - Interface fields: type A struct { b SomeInterface } -> stores interface, no concrete instantiation
|
|
163
|
+
// - Map types: type A map[K]V -> map initialized empty, no constructor calls
|
|
164
|
+
// - Array/slice of pointers: type A []*B -> array of pointers, no constructor calls
|
|
165
|
+
func (c *GoToTSCompiler) extractTypeDependencies(typeExpr ast.Expr, typeSpecMap map[string]*ast.TypeSpec) []string {
|
|
166
|
+
var deps []string
|
|
167
|
+
|
|
168
|
+
switch t := typeExpr.(type) {
|
|
169
|
+
case *ast.Ident:
|
|
170
|
+
// Direct type reference (e.g., type MyType OtherType)
|
|
171
|
+
if _, isLocalType := typeSpecMap[t.Name]; isLocalType {
|
|
172
|
+
deps = append(deps, t.Name)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
case *ast.StructType:
|
|
176
|
+
// Struct type - check field types, but be more selective
|
|
177
|
+
if t.Fields != nil {
|
|
178
|
+
for _, field := range t.Fields.List {
|
|
179
|
+
fieldDeps := c.extractStructFieldDependencies(field.Type, typeSpecMap)
|
|
180
|
+
deps = append(deps, fieldDeps...)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
case *ast.ArrayType:
|
|
185
|
+
// Array/slice type - only depends on element type if element is a struct (not pointer)
|
|
186
|
+
if !c.isPointerType(t.Elt) {
|
|
187
|
+
elemDeps := c.extractTypeDependencies(t.Elt, typeSpecMap)
|
|
188
|
+
deps = append(deps, elemDeps...)
|
|
189
|
+
}
|
|
190
|
+
// Arrays of pointers don't create initialization dependencies
|
|
191
|
+
|
|
192
|
+
case *ast.StarExpr:
|
|
193
|
+
// Pointer types don't create initialization dependencies
|
|
194
|
+
// The pointed-to type doesn't need to be initialized when creating a pointer field
|
|
195
|
+
|
|
196
|
+
case *ast.MapType:
|
|
197
|
+
// Map types don't create initialization dependencies
|
|
198
|
+
// Maps are initialized empty, no constructor calls needed
|
|
199
|
+
|
|
200
|
+
case *ast.InterfaceType:
|
|
201
|
+
// Interface types don't create initialization dependencies
|
|
202
|
+
|
|
203
|
+
case *ast.FuncType:
|
|
204
|
+
// Function types don't create initialization dependencies
|
|
205
|
+
|
|
206
|
+
case *ast.SelectorExpr:
|
|
207
|
+
// External package types don't create local dependencies
|
|
208
|
+
|
|
209
|
+
// Add other type expressions as needed
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Sort dependencies for deterministic output
|
|
213
|
+
sort.Strings(deps)
|
|
214
|
+
return deps
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// extractStructFieldDependencies extracts dependencies from struct field types
|
|
218
|
+
func (c *GoToTSCompiler) extractStructFieldDependencies(fieldType ast.Expr, typeSpecMap map[string]*ast.TypeSpec) []string {
|
|
219
|
+
var deps []string
|
|
220
|
+
|
|
221
|
+
switch t := fieldType.(type) {
|
|
222
|
+
case *ast.Ident:
|
|
223
|
+
// Direct field type: struct { b B } - this requires B to be initialized
|
|
224
|
+
if _, isLocalType := typeSpecMap[t.Name]; isLocalType {
|
|
225
|
+
deps = append(deps, t.Name)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
case *ast.StarExpr:
|
|
229
|
+
// Pointer field: struct { b *B } - this doesn't require B initialization
|
|
230
|
+
// Pointers are just references, no constructor call needed
|
|
231
|
+
|
|
232
|
+
case *ast.ArrayType:
|
|
233
|
+
// Array field: struct { b []B } or struct { b [5]B }
|
|
234
|
+
// Only create dependency if element type is not a pointer
|
|
235
|
+
if !c.isPointerType(t.Elt) {
|
|
236
|
+
elemDeps := c.extractTypeDependencies(t.Elt, typeSpecMap)
|
|
237
|
+
deps = append(deps, elemDeps...)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
case *ast.MapType:
|
|
241
|
+
// Map field: struct { b map[K]V } - maps don't require initialization dependencies
|
|
242
|
+
|
|
243
|
+
case *ast.InterfaceType:
|
|
244
|
+
// Interface field: struct { b SomeInterface } - no concrete type dependency
|
|
245
|
+
|
|
246
|
+
case *ast.FuncType:
|
|
247
|
+
// Function field: struct { b func() } - no dependency
|
|
248
|
+
|
|
249
|
+
// Handle other field types as needed
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return deps
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// sortVarSpecsByTypeDependencies sorts variable declarations based on their type dependencies
|
|
256
|
+
func (c *GoToTSCompiler) sortVarSpecsByTypeDependencies(varSpecs []*ast.ValueSpec, typeSpecs []*ast.TypeSpec) ([]*ast.ValueSpec, error) {
|
|
257
|
+
if len(varSpecs) <= 1 {
|
|
258
|
+
return varSpecs, nil
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Build type name map
|
|
262
|
+
typeSpecMap := make(map[string]*ast.TypeSpec)
|
|
263
|
+
for _, typeSpec := range typeSpecs {
|
|
264
|
+
typeSpecMap[typeSpec.Name.Name] = typeSpec
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Group variables by dependency status with names for sorting
|
|
268
|
+
type namedVarSpec struct {
|
|
269
|
+
spec *ast.ValueSpec
|
|
270
|
+
name string
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
var independentVars []namedVarSpec
|
|
274
|
+
var dependentVars []namedVarSpec
|
|
275
|
+
|
|
276
|
+
for _, varSpec := range varSpecs {
|
|
277
|
+
// Get variable name for sorting
|
|
278
|
+
varName := ""
|
|
279
|
+
if len(varSpec.Names) > 0 {
|
|
280
|
+
varName = varSpec.Names[0].Name
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
hasDependency := false
|
|
284
|
+
|
|
285
|
+
// Check type annotation
|
|
286
|
+
if varSpec.Type != nil {
|
|
287
|
+
deps := c.extractTypeDependencies(varSpec.Type, typeSpecMap)
|
|
288
|
+
if len(deps) > 0 {
|
|
289
|
+
hasDependency = true
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Check initializer expressions for type usage
|
|
294
|
+
if !hasDependency {
|
|
295
|
+
for _, value := range varSpec.Values {
|
|
296
|
+
if c.hasTypeReferences(value, typeSpecMap) {
|
|
297
|
+
hasDependency = true
|
|
298
|
+
break
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
namedVar := namedVarSpec{spec: varSpec, name: varName}
|
|
304
|
+
if hasDependency {
|
|
305
|
+
dependentVars = append(dependentVars, namedVar)
|
|
306
|
+
} else {
|
|
307
|
+
independentVars = append(independentVars, namedVar)
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Sort both groups by name for deterministic output
|
|
312
|
+
sort.Slice(independentVars, func(i, j int) bool {
|
|
313
|
+
return independentVars[i].name < independentVars[j].name
|
|
314
|
+
})
|
|
315
|
+
sort.Slice(dependentVars, func(i, j int) bool {
|
|
316
|
+
return dependentVars[i].name < dependentVars[j].name
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
// Return independent variables first, then dependent ones
|
|
320
|
+
result := make([]*ast.ValueSpec, 0, len(varSpecs))
|
|
321
|
+
for _, namedVar := range independentVars {
|
|
322
|
+
result = append(result, namedVar.spec)
|
|
323
|
+
}
|
|
324
|
+
for _, namedVar := range dependentVars {
|
|
325
|
+
result = append(result, namedVar.spec)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return result, nil
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// hasTypeReferences checks if an expression contains references to local types
|
|
332
|
+
func (c *GoToTSCompiler) hasTypeReferences(expr ast.Expr, typeSpecMap map[string]*ast.TypeSpec) bool {
|
|
333
|
+
hasRef := false
|
|
334
|
+
|
|
335
|
+
ast.Inspect(expr, func(n ast.Node) bool {
|
|
336
|
+
switch t := n.(type) {
|
|
337
|
+
case *ast.CallExpr:
|
|
338
|
+
// Check function calls like new Message(), makeChannel<Message>()
|
|
339
|
+
if ident, ok := t.Fun.(*ast.Ident); ok {
|
|
340
|
+
if _, isLocalType := typeSpecMap[ident.Name]; isLocalType {
|
|
341
|
+
hasRef = true
|
|
342
|
+
return false
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
// Check type arguments in generic calls
|
|
346
|
+
if funcType, ok := t.Fun.(*ast.IndexExpr); ok {
|
|
347
|
+
if c.hasTypeReferences(funcType.Index, typeSpecMap) {
|
|
348
|
+
hasRef = true
|
|
349
|
+
return false
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
case *ast.CompositeLit:
|
|
353
|
+
// Check composite literals like Message{...}
|
|
354
|
+
if ident, ok := t.Type.(*ast.Ident); ok {
|
|
355
|
+
if _, isLocalType := typeSpecMap[ident.Name]; isLocalType {
|
|
356
|
+
hasRef = true
|
|
357
|
+
return false
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
case *ast.Ident:
|
|
361
|
+
// Check direct type references
|
|
362
|
+
if _, isLocalType := typeSpecMap[t.Name]; isLocalType {
|
|
363
|
+
hasRef = true
|
|
364
|
+
return false
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return !hasRef // Stop walking if we found a reference
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
return hasRef
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// topologicalSort performs a topological sort of the dependency graph
|
|
374
|
+
func (c *GoToTSCompiler) topologicalSort(dependencies map[string][]string) ([]string, error) {
|
|
375
|
+
// Kahn's algorithm for topological sorting with deterministic ordering
|
|
376
|
+
inDegree := make(map[string]int)
|
|
377
|
+
graph := make(map[string][]string)
|
|
378
|
+
|
|
379
|
+
// Initialize in-degree counts and reverse graph
|
|
380
|
+
for node := range dependencies {
|
|
381
|
+
inDegree[node] = 0
|
|
382
|
+
graph[node] = []string{}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Build reverse graph and count in-degrees
|
|
386
|
+
for node, deps := range dependencies {
|
|
387
|
+
// Sort dependencies for consistent output
|
|
388
|
+
sortedDeps := make([]string, len(deps))
|
|
389
|
+
copy(sortedDeps, deps)
|
|
390
|
+
sort.Strings(sortedDeps)
|
|
391
|
+
|
|
392
|
+
for _, dep := range sortedDeps {
|
|
393
|
+
if _, exists := inDegree[dep]; exists {
|
|
394
|
+
graph[dep] = append(graph[dep], node)
|
|
395
|
+
inDegree[node]++
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Sort neighbors in graph for consistency
|
|
401
|
+
for node := range graph {
|
|
402
|
+
sort.Strings(graph[node])
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Find nodes with no incoming edges and sort them
|
|
406
|
+
var queue []string
|
|
407
|
+
for node, degree := range inDegree {
|
|
408
|
+
if degree == 0 {
|
|
409
|
+
queue = append(queue, node)
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
sort.Strings(queue) // Sort initial queue for deterministic output
|
|
413
|
+
|
|
414
|
+
var result []string
|
|
415
|
+
|
|
416
|
+
for len(queue) > 0 {
|
|
417
|
+
// Remove node from queue (already sorted)
|
|
418
|
+
current := queue[0]
|
|
419
|
+
queue = queue[1:]
|
|
420
|
+
result = append(result, current)
|
|
421
|
+
|
|
422
|
+
// Collect new zero-degree nodes
|
|
423
|
+
var newZeroNodes []string
|
|
424
|
+
for _, neighbor := range graph[current] {
|
|
425
|
+
inDegree[neighbor]--
|
|
426
|
+
if inDegree[neighbor] == 0 {
|
|
427
|
+
newZeroNodes = append(newZeroNodes, neighbor)
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Sort new zero-degree nodes and add to queue
|
|
432
|
+
sort.Strings(newZeroNodes)
|
|
433
|
+
queue = append(queue, newZeroNodes...)
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Check for cycles
|
|
437
|
+
if len(result) != len(dependencies) {
|
|
438
|
+
// Find the remaining nodes to help debug the circular dependency
|
|
439
|
+
processed := make(map[string]bool)
|
|
440
|
+
for _, name := range result {
|
|
441
|
+
processed[name] = true
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
var remaining []string
|
|
445
|
+
for name := range dependencies {
|
|
446
|
+
if !processed[name] {
|
|
447
|
+
remaining = append(remaining, name)
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
sort.Strings(remaining)
|
|
451
|
+
|
|
452
|
+
return nil, fmt.Errorf("circular dependency detected in type declarations. Remaining types: %v", remaining)
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return result, nil
|
|
456
|
+
}
|
|
457
|
+
|
|
47
458
|
// WriteFuncDeclAsFunction translates a Go function declaration (`ast.FuncDecl`)
|
|
48
459
|
// that does not have a receiver (i.e., it's a regular function, not a method)
|
|
49
460
|
// into a TypeScript function.
|
|
@@ -6,17 +6,13 @@ import (
|
|
|
6
6
|
"strings"
|
|
7
7
|
)
|
|
8
8
|
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
func (c *GoToTSCompiler) writeAsyncCallIfNeeded(exp *ast.CallExpr) bool {
|
|
9
|
+
// isCallExprAsync determines if a CallExpr represents an async function/method call
|
|
10
|
+
func (c *GoToTSCompiler) isCallExprAsync(exp *ast.CallExpr) bool {
|
|
12
11
|
switch fun := exp.Fun.(type) {
|
|
13
12
|
case *ast.Ident:
|
|
14
13
|
// Function call (e.g., func())
|
|
15
|
-
if obj := c.
|
|
16
|
-
|
|
17
|
-
c.tsw.WriteLiterally("await ")
|
|
18
|
-
return true
|
|
19
|
-
}
|
|
14
|
+
if obj := c.objectOfIdent(fun); obj != nil {
|
|
15
|
+
return c.analysis.IsAsyncFunc(obj)
|
|
20
16
|
}
|
|
21
17
|
return false
|
|
22
18
|
|
|
@@ -29,13 +25,13 @@ func (c *GoToTSCompiler) writeAsyncCallIfNeeded(exp *ast.CallExpr) bool {
|
|
|
29
25
|
switch x := fun.X.(type) {
|
|
30
26
|
case *ast.Ident:
|
|
31
27
|
// Direct identifier: obj.method()
|
|
32
|
-
obj = c.
|
|
28
|
+
obj = c.objectOfIdent(x)
|
|
33
29
|
objOk = obj != nil
|
|
34
30
|
|
|
35
31
|
case *ast.StarExpr:
|
|
36
32
|
// Pointer dereference: (*p).method() or p.method() where p is a pointer
|
|
37
33
|
if id, isIdent := x.X.(*ast.Ident); isIdent {
|
|
38
|
-
obj = c.
|
|
34
|
+
obj = c.objectOfIdent(id)
|
|
39
35
|
objOk = obj != nil
|
|
40
36
|
}
|
|
41
37
|
|
|
@@ -43,8 +39,6 @@ func (c *GoToTSCompiler) writeAsyncCallIfNeeded(exp *ast.CallExpr) bool {
|
|
|
43
39
|
// Field access: obj.field.method()
|
|
44
40
|
// Get the type of the field access expression
|
|
45
41
|
if fieldType := c.pkg.TypesInfo.TypeOf(x); fieldType != nil {
|
|
46
|
-
// For field access, we create a synthetic object representing the field type
|
|
47
|
-
// We'll handle this case below when we determine the method's type
|
|
48
42
|
objOk = true
|
|
49
43
|
}
|
|
50
44
|
|
|
@@ -61,13 +55,7 @@ func (c *GoToTSCompiler) writeAsyncCallIfNeeded(exp *ast.CallExpr) bool {
|
|
|
61
55
|
if pkgName, isPkg := obj.(*types.PkgName); isPkg {
|
|
62
56
|
methodName := fun.Sel.Name
|
|
63
57
|
pkgPath := pkgName.Imported().Path()
|
|
64
|
-
|
|
65
|
-
// Check if this package-level function is async (empty TypeName)
|
|
66
|
-
if c.analysis.IsMethodAsync(pkgPath, "", methodName) {
|
|
67
|
-
c.tsw.WriteLiterally("await ")
|
|
68
|
-
return true
|
|
69
|
-
}
|
|
70
|
-
return false
|
|
58
|
+
return c.analysis.IsMethodAsync(pkgPath, "", methodName)
|
|
71
59
|
}
|
|
72
60
|
}
|
|
73
61
|
|
|
@@ -83,7 +71,6 @@ func (c *GoToTSCompiler) writeAsyncCallIfNeeded(exp *ast.CallExpr) bool {
|
|
|
83
71
|
}
|
|
84
72
|
} else {
|
|
85
73
|
// Field access case: obj.field.method()
|
|
86
|
-
// Get the type of the field access expression
|
|
87
74
|
targetType = c.pkg.TypesInfo.TypeOf(fun.X)
|
|
88
75
|
if targetType == nil {
|
|
89
76
|
return false
|
|
@@ -94,12 +81,7 @@ func (c *GoToTSCompiler) writeAsyncCallIfNeeded(exp *ast.CallExpr) bool {
|
|
|
94
81
|
|
|
95
82
|
// Check if target type is an interface
|
|
96
83
|
if interfaceType, isInterface := targetType.Underlying().(*types.Interface); isInterface {
|
|
97
|
-
|
|
98
|
-
if c.analysis.IsInterfaceMethodAsync(interfaceType, methodName) {
|
|
99
|
-
c.tsw.WriteLiterally("await ")
|
|
100
|
-
return true
|
|
101
|
-
}
|
|
102
|
-
return false
|
|
84
|
+
return c.analysis.IsInterfaceMethodAsync(interfaceType, methodName)
|
|
103
85
|
}
|
|
104
86
|
|
|
105
87
|
// Get the named type from the target type
|
|
@@ -128,22 +110,26 @@ func (c *GoToTSCompiler) writeAsyncCallIfNeeded(exp *ast.CallExpr) bool {
|
|
|
128
110
|
if typePkg != nil {
|
|
129
111
|
pkgPath = typePkg.Path()
|
|
130
112
|
} else {
|
|
131
|
-
// Fallback to current package
|
|
132
113
|
pkgPath = c.pkg.Types.Path()
|
|
133
114
|
}
|
|
134
115
|
|
|
135
|
-
|
|
136
|
-
if c.analysis.IsMethodAsync(pkgPath, typeName, methodName) {
|
|
137
|
-
c.tsw.WriteLiterally("await ")
|
|
138
|
-
return true
|
|
139
|
-
}
|
|
140
|
-
return false
|
|
116
|
+
return c.analysis.IsMethodAsync(pkgPath, typeName, methodName)
|
|
141
117
|
|
|
142
118
|
default:
|
|
143
119
|
return false
|
|
144
120
|
}
|
|
145
121
|
}
|
|
146
122
|
|
|
123
|
+
// writeAsyncCallIfNeeded writes the await prefix for async function or method calls
|
|
124
|
+
// Returns true if await was written, false otherwise
|
|
125
|
+
func (c *GoToTSCompiler) writeAsyncCallIfNeeded(exp *ast.CallExpr) bool {
|
|
126
|
+
if c.isCallExprAsync(exp) {
|
|
127
|
+
c.tsw.WriteLiterally("await ")
|
|
128
|
+
return true
|
|
129
|
+
}
|
|
130
|
+
return false
|
|
131
|
+
}
|
|
132
|
+
|
|
147
133
|
// addNonNullAssertion adds ! for function calls that might return null
|
|
148
134
|
func (c *GoToTSCompiler) addNonNullAssertion(expFun ast.Expr) {
|
|
149
135
|
if funType := c.pkg.TypesInfo.TypeOf(expFun); funType != nil {
|
|
@@ -151,7 +137,7 @@ func (c *GoToTSCompiler) addNonNullAssertion(expFun ast.Expr) {
|
|
|
151
137
|
// Check if this is a function parameter identifier that needs not-null assertion
|
|
152
138
|
if ident, isIdent := expFun.(*ast.Ident); isIdent {
|
|
153
139
|
// Check if this identifier is a function parameter
|
|
154
|
-
if obj := c.
|
|
140
|
+
if obj := c.objectOfIdent(ident); obj != nil {
|
|
155
141
|
if _, isVar := obj.(*types.Var); isVar {
|
|
156
142
|
// This is a variable (including function parameters)
|
|
157
143
|
// Function parameters that are function types need ! assertion
|
|
@@ -8,6 +8,25 @@ import (
|
|
|
8
8
|
"github.com/pkg/errors"
|
|
9
9
|
)
|
|
10
10
|
|
|
11
|
+
// builtinFunctions is the definitive list of Go builtin functions handled by the compiler
|
|
12
|
+
var builtinFunctions = map[string]bool{
|
|
13
|
+
"len": true,
|
|
14
|
+
"cap": true,
|
|
15
|
+
"make": true,
|
|
16
|
+
"new": true,
|
|
17
|
+
"append": true,
|
|
18
|
+
"copy": true,
|
|
19
|
+
"delete": true,
|
|
20
|
+
"complex": true,
|
|
21
|
+
"real": true,
|
|
22
|
+
"imag": true,
|
|
23
|
+
"close": true,
|
|
24
|
+
"panic": true,
|
|
25
|
+
"recover": true,
|
|
26
|
+
"print": true,
|
|
27
|
+
"println": true,
|
|
28
|
+
}
|
|
29
|
+
|
|
11
30
|
// writeBuiltinFunction handles built-in Go functions
|
|
12
31
|
func (c *GoToTSCompiler) writeBuiltinFunction(exp *ast.CallExpr, funName string) (handled bool, err error) {
|
|
13
32
|
switch funName {
|
|
@@ -7,34 +7,6 @@ import (
|
|
|
7
7
|
"github.com/pkg/errors"
|
|
8
8
|
)
|
|
9
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
10
|
// writeByteSliceCreation handles the creation of []byte slices with proper Uint8Array handling
|
|
39
11
|
func (c *GoToTSCompiler) writeByteSliceCreation(lengthArg, capacityArg interface{}) error {
|
|
40
12
|
return c.writeSliceCreationForType(lengthArg, capacityArg, true)
|