goscript 0.0.58 → 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.
Files changed (44) hide show
  1. package/README.md +40 -33
  2. package/compiler/analysis.go +115 -19
  3. package/compiler/assignment.go +163 -217
  4. package/compiler/compiler.go +35 -31
  5. package/compiler/composite-lit.go +233 -196
  6. package/compiler/constraint.go +88 -0
  7. package/compiler/decl.go +82 -24
  8. package/compiler/expr-call-async.go +20 -34
  9. package/compiler/expr-call-builtins.go +19 -0
  10. package/compiler/expr-call-helpers.go +0 -28
  11. package/compiler/expr-call-make.go +93 -343
  12. package/compiler/expr-call-type-conversion.go +221 -249
  13. package/compiler/expr-call.go +70 -69
  14. package/compiler/expr-selector.go +21 -24
  15. package/compiler/expr.go +3 -60
  16. package/compiler/protobuf.go +180 -36
  17. package/compiler/spec-value.go +132 -24
  18. package/compiler/spec.go +14 -55
  19. package/compiler/stmt-assign.go +338 -356
  20. package/compiler/stmt-range.go +4 -24
  21. package/compiler/stmt.go +92 -203
  22. package/compiler/type-utils.go +185 -0
  23. package/compiler/type.go +26 -80
  24. package/dist/gs/builtin/slice.d.ts +1 -1
  25. package/dist/gs/builtin/slice.js +3 -0
  26. package/dist/gs/builtin/slice.js.map +1 -1
  27. package/dist/gs/builtin/type.js +8 -2
  28. package/dist/gs/builtin/type.js.map +1 -1
  29. package/dist/gs/fmt/fmt.js +113 -16
  30. package/dist/gs/fmt/fmt.js.map +1 -1
  31. package/dist/gs/runtime/runtime.d.ts +1 -1
  32. package/dist/gs/runtime/runtime.js +1 -1
  33. package/dist/gs/slices/slices.d.ts +23 -0
  34. package/dist/gs/slices/slices.js +61 -0
  35. package/dist/gs/slices/slices.js.map +1 -1
  36. package/go.mod +8 -8
  37. package/go.sum +14 -14
  38. package/gs/builtin/slice.ts +5 -2
  39. package/gs/builtin/type.ts +13 -6
  40. package/gs/fmt/fmt.test.ts +176 -0
  41. package/gs/fmt/fmt.ts +109 -18
  42. package/gs/runtime/runtime.ts +1 -1
  43. package/gs/slices/slices.ts +68 -0
  44. package/package.json +3 -3
@@ -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.pkg.TypesInfo.Uses[unaryExprXIdent]
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
- isWrapperType := c.analysis.IsNamedBasicType(namedType)
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.pkg.TypesInfo.Uses[expr]; obj != nil {
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
- isWrapperType := c.analysis.IsNamedBasicType(namedType)
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 (existing logic seems okay, but less common for pointers) ---
370
- c.tsw.WriteLiterally("let ")
371
- c.tsw.WriteLiterally("[") // Use array destructuring for multi-assign
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
- // TODO: handle other kinds of assignment += -= etc.
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 initializer, assign default values (complex for multi-assign)
403
- // For simplicity, assign default array based on context (needs improvement)
404
- c.tsw.WriteLiterally(" = []") // Placeholder
405
- // TODO: Assign correct zero values based on types
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.pkg.TypesInfo.Uses[rhsIdent]; rhsObj != nil {
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.analysis.IsNamedBasicType(fieldType) {
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) isStructValueType(fieldType types.Type) bool {
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
- // Fallback (should not happen if isImportedBasicType was correct)
243
- c.WriteZeroValueForType(fieldType)
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.analysis.IsNamedBasicType(named) {
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.analysis.IsNamedBasicType(alias) {
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 ")