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/spec-value.go
CHANGED
|
@@ -5,8 +5,81 @@ import (
|
|
|
5
5
|
"go/ast"
|
|
6
6
|
"go/token"
|
|
7
7
|
"go/types"
|
|
8
|
+
|
|
9
|
+
"golang.org/x/tools/go/packages"
|
|
8
10
|
)
|
|
9
11
|
|
|
12
|
+
// shouldApplyClone determines whether a `.clone()` method call should be appended
|
|
13
|
+
// to the TypeScript translation of a Go expression `rhs` when it appears on the
|
|
14
|
+
// right-hand side of an assignment. This is primarily to emulate Go's value
|
|
15
|
+
// semantics for struct assignments, where assigning one struct variable to another
|
|
16
|
+
// creates a copy of the struct.
|
|
17
|
+
//
|
|
18
|
+
// It uses `go/types` information (`pkg.TypesInfo`) to determine the type of `rhs`.
|
|
19
|
+
// - If `rhs` is identified as a struct type (either directly, as a named type
|
|
20
|
+
// whose underlying type is a struct, or an unnamed type whose underlying type
|
|
21
|
+
// is a struct), it returns `true`.
|
|
22
|
+
// - An optimization: if `rhs` is a composite literal (`*ast.CompositeLit`),
|
|
23
|
+
// it returns `false` because a composite literal already produces a new value,
|
|
24
|
+
// so cloning is unnecessary.
|
|
25
|
+
// - If type information is unavailable or `rhs` is not a struct type, it returns `false`.
|
|
26
|
+
//
|
|
27
|
+
// This function is crucial for ensuring that assignments of struct values in
|
|
28
|
+
// TypeScript behave like copies, as they do in Go, rather than reference assignments.
|
|
29
|
+
func shouldApplyClone(pkg *packages.Package, rhs ast.Expr) bool {
|
|
30
|
+
if pkg == nil || pkg.TypesInfo == nil {
|
|
31
|
+
// Cannot determine type without type info, default to no clone
|
|
32
|
+
return false
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Get the type of the RHS expression
|
|
36
|
+
var exprType types.Type
|
|
37
|
+
|
|
38
|
+
// Handle identifiers (variables) directly - the most common case
|
|
39
|
+
if ident, ok := rhs.(*ast.Ident); ok {
|
|
40
|
+
if obj := pkg.TypesInfo.Uses[ident]; obj != nil {
|
|
41
|
+
// Get the type directly from the object
|
|
42
|
+
exprType = obj.Type()
|
|
43
|
+
} else if obj := pkg.TypesInfo.Defs[ident]; obj != nil {
|
|
44
|
+
// Also check Defs map for definitions
|
|
45
|
+
exprType = obj.Type()
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// If we couldn't get the type from Uses/Defs, try getting it from Types
|
|
50
|
+
if exprType == nil {
|
|
51
|
+
if tv, found := pkg.TypesInfo.Types[rhs]; found && tv.Type != nil {
|
|
52
|
+
exprType = tv.Type
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// No type information available
|
|
57
|
+
if exprType == nil {
|
|
58
|
+
return false
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Optimization: If it's a composite literal for a struct, no need to clone
|
|
62
|
+
// as it's already a fresh value
|
|
63
|
+
if _, isCompositeLit := rhs.(*ast.CompositeLit); isCompositeLit {
|
|
64
|
+
return false
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Check if it's a struct type (directly, through named type, or underlying)
|
|
68
|
+
if named, ok := exprType.(*types.Named); ok {
|
|
69
|
+
if _, isStruct := named.Underlying().(*types.Struct); isStruct {
|
|
70
|
+
return true // Named struct type
|
|
71
|
+
}
|
|
72
|
+
} else if _, ok := exprType.(*types.Struct); ok {
|
|
73
|
+
return true // Direct struct type
|
|
74
|
+
} else if underlying := exprType.Underlying(); underlying != nil {
|
|
75
|
+
if _, isStruct := underlying.(*types.Struct); isStruct {
|
|
76
|
+
return true // Underlying is a struct
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return false // Not a struct, do not apply clone
|
|
81
|
+
}
|
|
82
|
+
|
|
10
83
|
// WriteValueSpec translates a Go value specification (`ast.ValueSpec`),
|
|
11
84
|
// which represents `var` or `const` declarations, into TypeScript `let`
|
|
12
85
|
// declarations.
|
|
@@ -209,7 +282,7 @@ func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
|
|
|
209
282
|
if unaryExprXIdent, ok := unaryExpr.X.(*ast.Ident); ok {
|
|
210
283
|
// Case: &variable
|
|
211
284
|
// Check if the variable is varrefed
|
|
212
|
-
innerObj := c.
|
|
285
|
+
innerObj := c.objectOfIdent(unaryExprXIdent)
|
|
213
286
|
needsVarRefOperand := innerObj != nil && c.analysis.NeedsVarRef(innerObj)
|
|
214
287
|
|
|
215
288
|
// If variable is varrefed, assign the varRef itself (variable)
|
|
@@ -236,8 +309,7 @@ func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
|
|
|
236
309
|
// Check if this is a named type with methods and the initializer is a basic value
|
|
237
310
|
if namedType, isNamed := goType.(*types.Named); isNamed {
|
|
238
311
|
// Check if this is a wrapper type first
|
|
239
|
-
|
|
240
|
-
if isWrapperType {
|
|
312
|
+
if c.isWrapperType(namedType) {
|
|
241
313
|
// For wrapper types, no constructor wrapping needed
|
|
242
314
|
if shouldApplyClone(c.pkg, initializerExpr) {
|
|
243
315
|
// When cloning for value assignment, mark the result as struct value
|
|
@@ -263,7 +335,7 @@ func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
|
|
|
263
335
|
// Check if it's a simple identifier (not a function call or complex expression)
|
|
264
336
|
if expr.Name != "nil" {
|
|
265
337
|
// Check if this identifier refers to a value of the underlying type
|
|
266
|
-
if obj := c.
|
|
338
|
+
if obj := c.objectOfIdent(expr); obj != nil {
|
|
267
339
|
if objType := obj.Type(); objType != nil {
|
|
268
340
|
// If the identifier's type matches the underlying type, wrap it
|
|
269
341
|
if types.Identical(objType, namedType.Underlying()) {
|
|
@@ -342,8 +414,7 @@ func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
|
|
|
342
414
|
// No initializer, use the zero value directly
|
|
343
415
|
// Check if this is a wrapper type first
|
|
344
416
|
if namedType, isNamed := goType.(*types.Named); isNamed {
|
|
345
|
-
|
|
346
|
-
if isWrapperType {
|
|
417
|
+
if c.isWrapperType(namedType) {
|
|
347
418
|
// For wrapper types, just use zero value directly
|
|
348
419
|
c.WriteZeroValueForType(goType)
|
|
349
420
|
} else {
|
|
@@ -366,19 +437,21 @@ func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
|
|
|
366
437
|
return nil
|
|
367
438
|
}
|
|
368
439
|
|
|
369
|
-
// --- Multi-variable declaration
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
for i, name := range a.Names {
|
|
373
|
-
if i != 0 {
|
|
374
|
-
c.tsw.WriteLiterally(", ")
|
|
375
|
-
}
|
|
376
|
-
c.tsw.WriteLiterally(c.sanitizeIdentifier(name.Name))
|
|
377
|
-
// TODO: Add type annotations for multi-var declarations if possible/needed
|
|
378
|
-
}
|
|
379
|
-
c.tsw.WriteLiterally("]")
|
|
440
|
+
// --- Multi-variable declaration with proper individual declarations ---
|
|
441
|
+
// Instead of using array destructuring which creates undefined types,
|
|
442
|
+
// generate individual variable declarations with proper types and zero values
|
|
380
443
|
if len(a.Values) > 0 {
|
|
381
|
-
//
|
|
444
|
+
// Has initializers - use array destructuring for multiple assignments
|
|
445
|
+
c.tsw.WriteLiterally("let ")
|
|
446
|
+
c.tsw.WriteLiterally("[") // Use array destructuring for multi-assign
|
|
447
|
+
for i, name := range a.Names {
|
|
448
|
+
if i != 0 {
|
|
449
|
+
c.tsw.WriteLiterally(", ")
|
|
450
|
+
}
|
|
451
|
+
c.tsw.WriteLiterally(c.sanitizeIdentifier(name.Name))
|
|
452
|
+
}
|
|
453
|
+
c.tsw.WriteLiterally("]")
|
|
454
|
+
|
|
382
455
|
c.tsw.WriteLiterally(" = ")
|
|
383
456
|
if len(a.Values) == 1 && len(a.Names) > 1 {
|
|
384
457
|
// Assign from a single multi-return value
|
|
@@ -398,13 +471,48 @@ func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
|
|
|
398
471
|
}
|
|
399
472
|
c.tsw.WriteLiterally("]")
|
|
400
473
|
}
|
|
474
|
+
c.tsw.WriteLine("")
|
|
401
475
|
} else {
|
|
402
|
-
// No
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
476
|
+
// No initializers - generate individual variable declarations with zero values
|
|
477
|
+
for _, name := range a.Names {
|
|
478
|
+
// Skip underscore variables
|
|
479
|
+
if name.Name == "_" {
|
|
480
|
+
continue
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
obj := c.pkg.TypesInfo.Defs[name]
|
|
484
|
+
if obj == nil {
|
|
485
|
+
return fmt.Errorf("could not resolve type for variable %v", name.Name)
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
goType := obj.Type()
|
|
489
|
+
|
|
490
|
+
// Check if exported and not inside function
|
|
491
|
+
isInsideFunction := false
|
|
492
|
+
if nodeInfo := c.analysis.NodeData[a]; nodeInfo != nil {
|
|
493
|
+
isInsideFunction = nodeInfo.IsInsideFunction
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if name.IsExported() && !isInsideFunction {
|
|
497
|
+
c.tsw.WriteLiterally("export ")
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
c.tsw.WriteLiterally("let ")
|
|
501
|
+
c.tsw.WriteLiterally(c.sanitizeIdentifier(name.Name))
|
|
502
|
+
c.tsw.WriteLiterally(": ")
|
|
503
|
+
|
|
504
|
+
// Write type annotation - use AST-based type if available, otherwise infer from goType
|
|
505
|
+
if a.Type != nil {
|
|
506
|
+
c.WriteTypeExpr(a.Type)
|
|
507
|
+
} else {
|
|
508
|
+
c.WriteGoType(goType, GoTypeContextGeneral)
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
c.tsw.WriteLiterally(" = ")
|
|
512
|
+
c.WriteZeroValueForType(goType)
|
|
513
|
+
c.tsw.WriteLine("")
|
|
514
|
+
}
|
|
406
515
|
}
|
|
407
|
-
c.tsw.WriteLine("") // Use WriteLine instead of WriteLine(";")
|
|
408
516
|
return nil
|
|
409
517
|
}
|
|
410
518
|
|
|
@@ -413,7 +521,7 @@ func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
|
|
|
413
521
|
func (c *GoToTSCompiler) writeInitializerForInterface(initializerExpr ast.Expr, goType types.Type) error {
|
|
414
522
|
// Check if this is a pointer variable assigned to interface
|
|
415
523
|
if rhsIdent, isIdent := initializerExpr.(*ast.Ident); isIdent {
|
|
416
|
-
if rhsObj := c.
|
|
524
|
+
if rhsObj := c.objectOfIdent(rhsIdent); rhsObj != nil {
|
|
417
525
|
// Check if LHS is interface and RHS is pointer
|
|
418
526
|
if _, isInterface := goType.Underlying().(*types.Interface); isInterface {
|
|
419
527
|
if _, isPtr := rhsObj.Type().(*types.Pointer); isPtr {
|
package/compiler/spec.go
CHANGED
|
@@ -6,7 +6,6 @@ import (
|
|
|
6
6
|
"go/types"
|
|
7
7
|
"strings"
|
|
8
8
|
|
|
9
|
-
// types provides type information for Go types.
|
|
10
9
|
"github.com/pkg/errors"
|
|
11
10
|
)
|
|
12
11
|
|
|
@@ -137,7 +136,7 @@ func (c *GoToTSCompiler) writeRegularFieldInitializer(fieldName string, fieldTyp
|
|
|
137
136
|
c.tsw.WriteLiterallyf("init?.%s ?? ", fieldName)
|
|
138
137
|
|
|
139
138
|
// Priority 1: Check if this is a wrapper type
|
|
140
|
-
if c.
|
|
139
|
+
if c.isWrapperType(fieldType) {
|
|
141
140
|
// For wrapper types, use the zero value of the underlying type with type casting
|
|
142
141
|
if named, ok := fieldType.(*types.Named); ok {
|
|
143
142
|
c.WriteZeroValueForType(named.Underlying())
|
|
@@ -156,7 +155,11 @@ func (c *GoToTSCompiler) writeRegularFieldInitializer(fieldName string, fieldTyp
|
|
|
156
155
|
|
|
157
156
|
// Priority 2: Handle imported types with basic underlying types (like os.FileMode)
|
|
158
157
|
if c.isImportedBasicType(fieldType) {
|
|
159
|
-
c.writeImportedBasicTypeZeroValue(fieldType)
|
|
158
|
+
if err := c.writeImportedBasicTypeZeroValue(fieldType); err != nil {
|
|
159
|
+
// Emit diagnostic and conservative zero value to avoid silent fallback
|
|
160
|
+
c.tsw.WriteCommentInlinef(" writeImportedBasicTypeZeroValue error: %v ", err)
|
|
161
|
+
c.WriteZeroValueForType(fieldType)
|
|
162
|
+
}
|
|
160
163
|
return
|
|
161
164
|
}
|
|
162
165
|
|
|
@@ -175,59 +178,14 @@ func (c *GoToTSCompiler) writeRegularFieldInitializer(fieldName string, fieldTyp
|
|
|
175
178
|
c.WriteZeroValueForType(fieldType)
|
|
176
179
|
}
|
|
177
180
|
|
|
178
|
-
func (c *GoToTSCompiler)
|
|
179
|
-
if named, ok := fieldType.(*types.Named); ok {
|
|
180
|
-
if _, isStruct := named.Underlying().(*types.Struct); isStruct {
|
|
181
|
-
return true
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
return false
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
func (c *GoToTSCompiler) isImportedBasicType(fieldType types.Type) bool {
|
|
188
|
-
// Handle named types
|
|
189
|
-
if named, isNamed := fieldType.(*types.Named); isNamed {
|
|
190
|
-
obj := named.Obj()
|
|
191
|
-
if obj == nil || obj.Pkg() == nil || obj.Pkg() == c.pkg.Types {
|
|
192
|
-
return false // Not imported or is local
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
underlying := named.Underlying()
|
|
196
|
-
if underlying == nil {
|
|
197
|
-
return false
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
_, isBasic := underlying.(*types.Basic)
|
|
201
|
-
return isBasic
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Handle type aliases (like os.FileMode = fs.FileMode)
|
|
205
|
-
if alias, isAlias := fieldType.(*types.Alias); isAlias {
|
|
206
|
-
obj := alias.Obj()
|
|
207
|
-
if obj == nil || obj.Pkg() == nil || obj.Pkg() == c.pkg.Types {
|
|
208
|
-
return false // Not imported or is local
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
underlying := alias.Underlying()
|
|
212
|
-
if underlying == nil {
|
|
213
|
-
return false
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
_, isBasic := underlying.(*types.Basic)
|
|
217
|
-
return isBasic
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return false
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
func (c *GoToTSCompiler) writeImportedBasicTypeZeroValue(fieldType types.Type) {
|
|
181
|
+
func (c *GoToTSCompiler) writeImportedBasicTypeZeroValue(fieldType types.Type) error {
|
|
224
182
|
if named, ok := fieldType.(*types.Named); ok {
|
|
225
183
|
underlying := named.Underlying()
|
|
226
184
|
// Write zero value of underlying type with type casting
|
|
227
185
|
c.WriteZeroValueForType(underlying)
|
|
228
186
|
c.tsw.WriteLiterally(" as ")
|
|
229
187
|
c.WriteGoType(fieldType, GoTypeContextGeneral)
|
|
230
|
-
return
|
|
188
|
+
return nil
|
|
231
189
|
}
|
|
232
190
|
|
|
233
191
|
if alias, ok := fieldType.(*types.Alias); ok {
|
|
@@ -236,16 +194,17 @@ func (c *GoToTSCompiler) writeImportedBasicTypeZeroValue(fieldType types.Type) {
|
|
|
236
194
|
c.WriteZeroValueForType(underlying)
|
|
237
195
|
c.tsw.WriteLiterally(" as ")
|
|
238
196
|
c.WriteGoType(fieldType, GoTypeContextGeneral)
|
|
239
|
-
return
|
|
197
|
+
return nil
|
|
240
198
|
}
|
|
241
199
|
|
|
242
|
-
//
|
|
243
|
-
|
|
200
|
+
// Reaching here indicates an internal inconsistency between isImportedBasicType and field kind
|
|
201
|
+
// Return an error to surface the issue to callers
|
|
202
|
+
return fmt.Errorf("writeImportedBasicTypeZeroValue: unexpected non-named/alias type %T", fieldType)
|
|
244
203
|
}
|
|
245
204
|
|
|
246
205
|
func (c *GoToTSCompiler) writeNamedTypeZeroValue(named *types.Named) {
|
|
247
206
|
// Check if this is a wrapper type first
|
|
248
|
-
if c.
|
|
207
|
+
if c.isWrapperType(named) {
|
|
249
208
|
// For wrapper types, use the zero value of the underlying type with type casting
|
|
250
209
|
c.WriteZeroValueForType(named.Underlying())
|
|
251
210
|
c.tsw.WriteLiterally(" as ")
|
|
@@ -275,7 +234,7 @@ func (c *GoToTSCompiler) writeNamedTypeZeroValue(named *types.Named) {
|
|
|
275
234
|
|
|
276
235
|
func (c *GoToTSCompiler) writeTypeAliasZeroValue(alias *types.Alias, astType ast.Expr) {
|
|
277
236
|
// Check if this is a wrapper type first
|
|
278
|
-
if c.
|
|
237
|
+
if c.isWrapperType(alias) {
|
|
279
238
|
// For wrapper types, use the zero value of the underlying type with type casting
|
|
280
239
|
c.WriteZeroValueForType(alias.Underlying())
|
|
281
240
|
c.tsw.WriteLiterally(" as ")
|