goscript 0.0.22 → 0.0.24

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 (66) hide show
  1. package/README.md +1 -1
  2. package/cmd/goscript/cmd_compile.go +3 -3
  3. package/compiler/analysis.go +302 -182
  4. package/compiler/analysis_test.go +220 -0
  5. package/compiler/assignment.go +42 -43
  6. package/compiler/builtin_test.go +102 -0
  7. package/compiler/compiler.go +117 -29
  8. package/compiler/compiler_test.go +36 -8
  9. package/compiler/composite-lit.go +133 -53
  10. package/compiler/config.go +7 -3
  11. package/compiler/config_test.go +6 -33
  12. package/compiler/decl.go +36 -0
  13. package/compiler/expr-call.go +116 -60
  14. package/compiler/expr-selector.go +88 -43
  15. package/compiler/expr-star.go +57 -65
  16. package/compiler/expr-type.go +132 -5
  17. package/compiler/expr-value.go +8 -38
  18. package/compiler/expr.go +326 -30
  19. package/compiler/field.go +3 -3
  20. package/compiler/lit.go +34 -2
  21. package/compiler/primitive.go +19 -12
  22. package/compiler/spec-struct.go +140 -9
  23. package/compiler/spec-value.go +119 -41
  24. package/compiler/spec.go +21 -6
  25. package/compiler/stmt-assign.go +65 -3
  26. package/compiler/stmt-for.go +11 -0
  27. package/compiler/stmt-range.go +119 -11
  28. package/compiler/stmt-select.go +211 -0
  29. package/compiler/stmt-type-switch.go +147 -0
  30. package/compiler/stmt.go +175 -238
  31. package/compiler/type-assert.go +125 -379
  32. package/compiler/type.go +216 -129
  33. package/dist/gs/builtin/builtin.js +37 -0
  34. package/dist/gs/builtin/builtin.js.map +1 -0
  35. package/dist/gs/builtin/channel.js +471 -0
  36. package/dist/gs/builtin/channel.js.map +1 -0
  37. package/dist/gs/builtin/defer.js +54 -0
  38. package/dist/gs/builtin/defer.js.map +1 -0
  39. package/dist/gs/builtin/io.js +15 -0
  40. package/dist/gs/builtin/io.js.map +1 -0
  41. package/dist/gs/builtin/map.js +44 -0
  42. package/dist/gs/builtin/map.js.map +1 -0
  43. package/dist/gs/builtin/slice.js +799 -0
  44. package/dist/gs/builtin/slice.js.map +1 -0
  45. package/dist/gs/builtin/type.js +745 -0
  46. package/dist/gs/builtin/type.js.map +1 -0
  47. package/dist/gs/builtin/varRef.js +14 -0
  48. package/dist/gs/builtin/varRef.js.map +1 -0
  49. package/dist/gs/context/context.js +55 -0
  50. package/dist/gs/context/context.js.map +1 -0
  51. package/dist/gs/context/index.js +2 -0
  52. package/dist/gs/context/index.js.map +1 -0
  53. package/dist/gs/runtime/index.js +2 -0
  54. package/dist/gs/runtime/index.js.map +1 -0
  55. package/dist/gs/runtime/runtime.js +158 -0
  56. package/dist/gs/runtime/runtime.js.map +1 -0
  57. package/dist/gs/time/index.js +2 -0
  58. package/dist/gs/time/index.js.map +1 -0
  59. package/dist/gs/time/time.js +115 -0
  60. package/dist/gs/time/time.js.map +1 -0
  61. package/package.json +7 -6
  62. package/builtin/builtin.go +0 -11
  63. package/builtin/builtin.ts +0 -2379
  64. package/dist/builtin/builtin.d.ts +0 -513
  65. package/dist/builtin/builtin.js +0 -1686
  66. package/dist/builtin/builtin.js.map +0 -1
@@ -4,6 +4,7 @@ import (
4
4
  "fmt"
5
5
  "go/ast"
6
6
  "go/types"
7
+ "sort"
7
8
  "strings"
8
9
  )
9
10
 
@@ -11,7 +12,7 @@ import (
11
12
  // It handles the generation of:
12
13
  // - The class declaration.
13
14
  // - Getters and setters for all fields (both direct and embedded).
14
- // - The internal `_fields` property, which stores field values in `$.Box` containers
15
+ // - The internal `_fields` property, which stores field values in `$.VarRef` containers
15
16
  // to maintain Go's value semantics.
16
17
  // - A constructor that initializes the `_fields` and allows partial initialization.
17
18
  // - A `clone` method for creating a deep copy of the struct instance.
@@ -23,6 +24,12 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
23
24
  if err := c.WriteValueExpr(a.Name); err != nil {
24
25
  return err
25
26
  }
27
+
28
+ // Write type parameters if present (for generics)
29
+ if a.TypeParams != nil {
30
+ c.WriteTypeParameters(a.TypeParams)
31
+ }
32
+
26
33
  c.tsw.WriteLiterally(" ")
27
34
  c.tsw.WriteLine("{")
28
35
  c.tsw.Indent(1)
@@ -76,7 +83,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
76
83
  fieldKeyName = field.Name()
77
84
  }
78
85
  fieldTsType := c.getTypeString(field.Type())
79
- c.tsw.WriteLinef("%s: $.Box<%s>;", fieldKeyName, fieldTsType)
86
+ c.tsw.WriteLinef("%s: $.VarRef<%s>;", fieldKeyName, fieldTsType)
80
87
  }
81
88
  c.tsw.Indent(-1)
82
89
  c.tsw.WriteLine("}")
@@ -104,7 +111,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
104
111
  fieldKeyName = field.Name()
105
112
  }
106
113
 
107
- c.writeBoxedFieldInitializer(fieldKeyName, fieldType, field.Anonymous())
114
+ c.writeVarRefedFieldInitializer(fieldKeyName, fieldType, field.Anonymous())
108
115
 
109
116
  if i < numFields-1 {
110
117
  c.tsw.WriteLine(",")
@@ -121,9 +128,25 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
121
128
  c.tsw.WriteLine("")
122
129
 
123
130
  // Generate the clone method
124
- c.tsw.WriteLinef("public clone(): %s {", className)
131
+ cloneReturnType := className
132
+ if a.TypeParams != nil && len(a.TypeParams.List) > 0 {
133
+ cloneReturnType += "<"
134
+ first := true
135
+ for _, field := range a.TypeParams.List {
136
+ for _, name := range field.Names {
137
+ if !first {
138
+ cloneReturnType += ", "
139
+ }
140
+ first = false
141
+ cloneReturnType += name.Name
142
+ }
143
+ }
144
+ cloneReturnType += ">"
145
+ }
146
+
147
+ c.tsw.WriteLinef("public clone(): %s {", cloneReturnType)
125
148
  c.tsw.Indent(1)
126
- c.tsw.WriteLinef("const cloned = new %s()", className)
149
+ c.tsw.WriteLinef("const cloned = new %s()", cloneReturnType)
127
150
  c.tsw.WriteLine("cloned._fields = {")
128
151
  c.tsw.Indent(1)
129
152
 
@@ -164,7 +187,18 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
164
187
  if starExpr, ok := recvType.(*ast.StarExpr); ok {
165
188
  recvType = starExpr.X
166
189
  }
167
- if ident, ok := recvType.(*ast.Ident); ok && ident.Name == className {
190
+
191
+ // Check for both simple identifiers (Pair) and generic types (Pair[T])
192
+ var recvTypeName string
193
+ if ident, ok := recvType.(*ast.Ident); ok {
194
+ recvTypeName = ident.Name
195
+ } else if indexExpr, ok := recvType.(*ast.IndexExpr); ok {
196
+ if ident, ok := indexExpr.X.(*ast.Ident); ok {
197
+ recvTypeName = ident.Name
198
+ }
199
+ }
200
+
201
+ if recvTypeName == className {
168
202
  c.tsw.WriteLine("")
169
203
  if err := c.WriteFuncDeclAsMethod(funcDecl); err != nil {
170
204
  return err
@@ -292,21 +326,21 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
292
326
  }
293
327
  c.tsw.WriteLiterally(paramName)
294
328
  c.tsw.WriteLiterally(": ")
295
- c.WriteGoType(param.Type())
329
+ c.WriteGoType(param.Type(), GoTypeContextGeneral)
296
330
  }
297
331
  c.tsw.WriteLiterally(")")
298
332
  results := sig.Results()
299
333
  if results.Len() > 0 {
300
334
  c.tsw.WriteLiterally(": ")
301
335
  if results.Len() == 1 {
302
- c.WriteGoType(results.At(0).Type())
336
+ c.WriteGoType(results.At(0).Type(), GoTypeContextFunctionReturn)
303
337
  } else {
304
338
  c.tsw.WriteLiterally("[")
305
339
  for j := 0; j < results.Len(); j++ {
306
340
  if j > 0 {
307
341
  c.tsw.WriteLiterally(", ")
308
342
  }
309
- c.WriteGoType(results.At(j).Type())
343
+ c.WriteGoType(results.At(j).Type(), GoTypeContextFunctionReturn)
310
344
  }
311
345
  c.tsw.WriteLiterally("]")
312
346
  }
@@ -380,3 +414,100 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
380
414
  c.tsw.WriteLine("}")
381
415
  return nil
382
416
  }
417
+
418
+ // generateFlattenedInitTypeString generates a TypeScript type string for the
419
+ // initialization object passed to a Go struct's constructor (`_init` method in TypeScript).
420
+ // The generated type is a `Partial`-like structure, `"{ Field1?: Type1, Field2?: Type2, ... }"`,
421
+ // including all direct and promoted fields of the `structType`.
422
+ // - It iterates through the direct fields of the `structType`. Exported fields
423
+ // are included with their TypeScript types (obtained via `WriteGoType`).
424
+ // - For anonymous (embedded) fields, it generates a type like `EmbeddedName?: ConstructorParameters<typeof EmbeddedName>[0]`,
425
+ // allowing initialization of the embedded struct's fields directly within the outer struct's initializer.
426
+ // - It then uses `types.NewMethodSet` and checks for `types.Var` objects that are fields
427
+ // to find promoted fields from embedded structs, adding them to the type string if not already present.
428
+ //
429
+ // The resulting string is sorted by field name for deterministic output and represents
430
+ // the shape of the object expected by the struct's TypeScript constructor.
431
+ func (c *GoToTSCompiler) generateFlattenedInitTypeString(structType *types.Named) string {
432
+ if structType == nil {
433
+ return "{}"
434
+ }
435
+
436
+ // Use a map to collect unique field names and their types
437
+ fieldMap := make(map[string]string)
438
+ embeddedTypeMap := make(map[string]string) // Stores TS type string for embedded struct initializers
439
+
440
+ underlying, ok := structType.Underlying().(*types.Struct)
441
+ if !ok {
442
+ return "{}"
443
+ }
444
+
445
+ // First add the direct fields and track embedded types
446
+ for i := 0; i < underlying.NumFields(); i++ {
447
+ field := underlying.Field(i)
448
+ fieldName := field.Name()
449
+
450
+ if !field.Exported() && field.Pkg() != c.pkg.Types {
451
+ continue
452
+ }
453
+
454
+ if field.Anonymous() {
455
+ fieldType := field.Type()
456
+ isPtr := false
457
+ if ptr, ok := fieldType.(*types.Pointer); ok {
458
+ fieldType = ptr.Elem()
459
+ isPtr = true
460
+ }
461
+
462
+ if named, ok := fieldType.(*types.Named); ok {
463
+ embeddedName := named.Obj().Name()
464
+ // For embedded structs, the init type should allow providing fields of the embedded struct,
465
+ // or the embedded struct itself.
466
+ // We generate Partial<EmbeddedStructFields> | EmbeddedStructType
467
+ // This is complex. For now, let's use ConstructorParameters as before for simplicity,
468
+ // or allow the embedded struct type itself.
469
+ // The example `Person?: ConstructorParameters<typeof Person>[0]` is for the fields.
470
+ // Let's try to generate a type that allows either the embedded struct instance or its fields.
471
+ // This might be: `Partial<FlattenedInitType<EmbeddedName>> | EmbeddedName`
472
+ // For now, stick to the simpler `ConstructorParameters<typeof %s>[0]` which implies field-based init.
473
+ // Or, more simply, the type of the embedded struct itself if it's a pointer.
474
+ if isPtr {
475
+ embeddedTypeMap[c.getEmbeddedFieldKeyName(field.Type())] = c.getTypeString(field.Type()) // MyEmbeddedType | null
476
+ } else {
477
+ // For value-type embedded structs, allow initializing with its fields.
478
+ // This requires getting the flattened init type for the embedded struct.
479
+ // This could lead to recursion if not handled carefully.
480
+ // A simpler approach for now: use the embedded struct's own type.
481
+ // embeddedTypeMap[c.getEmbeddedFieldKeyName(field.Type())] = c.getTypeString(field.Type())
482
+ // Or, using ConstructorParameters to allow field-based initialization:
483
+ embeddedTypeMap[c.getEmbeddedFieldKeyName(field.Type())] = fmt.Sprintf("Partial<ConstructorParameters<typeof %s>[0]>", embeddedName)
484
+ }
485
+ }
486
+ continue
487
+ }
488
+ fieldMap[fieldName] = c.getTypeString(field.Type())
489
+ }
490
+
491
+ // Promoted fields (handled by Go's embedding, init should use direct/embedded names)
492
+ // The current logic for `generateFlattenedInitTypeString` seems to focus on top-level
493
+ // settable properties in the constructor. Promoted fields are accessed via `this.promotedField`,
494
+ // not typically set directly in `init?` unless the embedded struct itself is named in `init?`.
495
+
496
+ // Add embedded types to the field map (these are the names of the embedded structs themselves)
497
+ for embeddedName, embeddedTSType := range embeddedTypeMap {
498
+ fieldMap[embeddedName] = embeddedTSType
499
+ }
500
+
501
+ var fieldNames []string
502
+ for name := range fieldMap {
503
+ fieldNames = append(fieldNames, name)
504
+ }
505
+ sort.Strings(fieldNames)
506
+
507
+ var fieldDefs []string
508
+ for _, fieldName := range fieldNames {
509
+ fieldDefs = append(fieldDefs, fmt.Sprintf("%s?: %s", fieldName, fieldMap[fieldName]))
510
+ }
511
+
512
+ return "{" + strings.Join(fieldDefs, ", ") + "}"
513
+ }
@@ -1,10 +1,10 @@
1
1
  package compiler
2
2
 
3
3
  import (
4
+ "fmt"
4
5
  "go/ast"
5
6
  "go/token"
6
-
7
- "github.com/pkg/errors"
7
+ "go/types"
8
8
  )
9
9
 
10
10
  // WriteValueSpec translates a Go value specification (`ast.ValueSpec`),
@@ -12,13 +12,12 @@ import (
12
12
  // declarations.
13
13
  //
14
14
  // For single variable declarations (`var x T = val` or `var x = val` or `var x T`):
15
- // - It determines if the variable `x` needs to be boxed (e.g., if its address is taken)
16
- // using `c.analysis.NeedsBoxed(obj)`.
17
- // - If boxed: `let x: $.Box<T_ts> = $.box(initializer_ts_or_zero_ts);`
18
- // The type annotation is `$.Box<T_ts>`, and the initializer is wrapped in `$.box()`.
19
- // - If not boxed: `let x: T_ts = initializer_ts_or_zero_ts;`
20
- // The type annotation is `T_ts`. If the initializer is `&unboxedVar`, it becomes `$.box(unboxedVar_ts)`.
21
- // If the RHS is a struct value, `.clone()` is applied to maintain Go's value semantics.
15
+ // - It determines if the variable `x` needs to be varrefed (e.g., if its address is taken)
16
+ // using `c.analysis.NeedsVarRef(obj)`.
17
+ // - If variable referenced: `let x: $.VarRef<T_ts> = $.varRef(initializer_ts_or_zero_ts);`
18
+ // The type annotation is `$.VarRef<T_ts>`, and the initializer is wrapped in `$.varRef()`.
19
+ // - If not variable referenced: `let x: T_ts = initializer_ts_or_zero_ts;`
20
+ // The type annotation is `T_ts`. If the initializer is `&unvarrefedVar`, it becomes `$.varRef(unvarrefedVar_ts)`.
22
21
  // - If no initializer is provided, the TypeScript zero value (from `WriteZeroValueForType`)
23
22
  // is used.
24
23
  // - Type `T` (or `T_ts`) is obtained from `obj.Type()` and translated via `WriteGoType`.
@@ -44,36 +43,114 @@ func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
44
43
  name := a.Names[0]
45
44
  obj := c.pkg.TypesInfo.Defs[name]
46
45
  if obj == nil {
47
- return errors.Errorf("could not resolve type: %v", name)
46
+ return fmt.Errorf("could not resolve type: %v", name)
48
47
  }
49
48
 
50
49
  goType := obj.Type()
51
- needsBox := c.analysis.NeedsBoxed(obj) // Check if address is taken
50
+ needsVarRef := c.analysis.NeedsVarRef(obj) // Check if address is taken
51
+
52
+ hasInitializer := len(a.Values) > 0
53
+ var initializerExpr ast.Expr
54
+ if hasInitializer {
55
+ initializerExpr = a.Values[0]
56
+ }
57
+
58
+ // Check if the initializer will result in an $.arrayToSlice call in TypeScript
59
+ isSliceConversion := false
60
+ if hasInitializer {
61
+ // Case 1: Direct call to $.arrayToSlice in Go source (less common for typical array literals)
62
+ if callExpr, isCallExpr := initializerExpr.(*ast.CallExpr); isCallExpr {
63
+ if selExpr, isSelExpr := callExpr.Fun.(*ast.SelectorExpr); isSelExpr {
64
+ if pkgIdent, isPkgIdent := selExpr.X.(*ast.Ident); isPkgIdent && pkgIdent.Name == "$" {
65
+ if selExpr.Sel.Name == "arrayToSlice" {
66
+ isSliceConversion = true
67
+ }
68
+ }
69
+ }
70
+ }
71
+
72
+ // Case 2: Go array or slice literal, which will be compiled to $.arrayToSlice
73
+ // We also check if the original Go type is actually a slice or array.
74
+ if !isSliceConversion { // Only check if not already determined by Case 1
75
+ if _, isCompositeLit := initializerExpr.(*ast.CompositeLit); isCompositeLit {
76
+ switch goType.Underlying().(type) {
77
+ case *types.Slice, *types.Array:
78
+ isSliceConversion = true
79
+ }
80
+ }
81
+ }
82
+ }
52
83
 
53
84
  // Start declaration
54
85
  c.tsw.WriteLiterally("let ")
55
86
  c.tsw.WriteLiterally(name.Name)
56
- c.tsw.WriteLiterally(": ")
57
-
58
- // Write type annotation
59
- if needsBox {
60
- // If boxed, the variable holds Box<OriginalGoType>
61
- c.tsw.WriteLiterally("$.Box<")
62
- c.WriteGoType(goType) // Write the original Go type T
63
- c.tsw.WriteLiterally(">")
64
- } else {
65
- // If not boxed, the variable holds the translated Go type directly
66
- c.WriteGoType(goType)
87
+
88
+ // Write type annotation if:
89
+ // 1. Not a slice conversion (normal case), OR
90
+ // 2. Is a slice conversion but needs varRefing (we need explicit type for $.varRef())
91
+ if !isSliceConversion || needsVarRef {
92
+ c.tsw.WriteLiterally(": ")
93
+ // Write type annotation
94
+ if needsVarRef {
95
+ // If varrefed, the variable holds VarRef<OriginalGoType>
96
+ c.tsw.WriteLiterally("$.VarRef<")
97
+
98
+ // Special case: if this is a slice conversion from an array type,
99
+ // we should use the slice type instead of the array type
100
+ if isSliceConversion {
101
+ if arrayType, isArray := goType.Underlying().(*types.Array); isArray {
102
+ // Convert [N]T to $.Slice<T>
103
+ c.tsw.WriteLiterally("$.Slice<")
104
+ c.WriteGoType(arrayType.Elem(), GoTypeContextGeneral)
105
+ c.tsw.WriteLiterally(">")
106
+ } else {
107
+ // For slice types, write as-is (already $.Slice<T>)
108
+ c.WriteGoType(goType, GoTypeContextGeneral)
109
+ }
110
+ } else {
111
+ c.WriteGoType(goType, GoTypeContextGeneral) // Write the original Go type T
112
+ }
113
+ c.tsw.WriteLiterally(">")
114
+ } else {
115
+ // If not varrefed, the variable holds the translated Go type directly
116
+ // Custom logic for non-var-ref'd pointers to structs/interfaces.
117
+ if ptrType, isPtr := goType.(*types.Pointer); isPtr {
118
+ elemType := ptrType.Elem()
119
+ actualElemType := elemType.Underlying() // Get the true underlying type (e.g., struct, interface, basic)
120
+
121
+ isStruct := false
122
+ if _, ok := actualElemType.(*types.Struct); ok {
123
+ isStruct = true
124
+ }
125
+
126
+ isInterface := false
127
+ if _, ok := actualElemType.(*types.Interface); ok {
128
+ isInterface = true
129
+ }
130
+
131
+ if isStruct || isInterface {
132
+ // For non-var-ref'd pointers to structs or interfaces,
133
+ // the type is T | null, not $.VarRef<T> | null.
134
+ c.WriteGoType(elemType, GoTypeContextGeneral) // Write the element type itself (e.g., MyStruct)
135
+ c.tsw.WriteLiterally(" | null")
136
+ } else {
137
+ // For other pointer types (e.g., *int, *string, *[]int, **MyStruct),
138
+ // or pointers to types that are not structs/interfaces,
139
+ // use the standard pointer type translation.
140
+ c.WriteGoType(goType, GoTypeContextGeneral)
141
+ }
142
+ } else {
143
+ // Not a pointer type, write as is.
144
+ c.WriteGoType(goType, GoTypeContextGeneral)
145
+ }
146
+ }
67
147
  }
68
148
 
69
149
  // Write initializer
70
150
  c.tsw.WriteLiterally(" = ")
71
- hasInitializer := len(a.Values) > 0
72
- var initializerExpr ast.Expr
73
- if hasInitializer {
74
- initializerExpr = a.Values[0]
75
151
 
76
- // Special case for nil pointer to struct type: (*struct{})(nil)
152
+ // Special case for nil pointer to struct type: (*struct{})(nil)
153
+ if hasInitializer {
77
154
  if callExpr, isCallExpr := initializerExpr.(*ast.CallExpr); isCallExpr {
78
155
  if starExpr, isStarExpr := callExpr.Fun.(*ast.StarExpr); isStarExpr {
79
156
  if _, isStructType := starExpr.X.(*ast.StructType); isStructType {
@@ -81,6 +158,7 @@ func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
81
158
  if len(callExpr.Args) == 1 {
82
159
  if nilIdent, isIdent := callExpr.Args[0].(*ast.Ident); isIdent && nilIdent.Name == "nil" {
83
160
  c.tsw.WriteLiterally("null")
161
+ c.tsw.WriteLine("") // Ensure newline after null
84
162
  return nil
85
163
  }
86
164
  }
@@ -89,41 +167,41 @@ func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
89
167
  }
90
168
  }
91
169
 
92
- if needsBox {
93
- // Boxed variable: let v: Box<T> = $.box(init_or_zero);
94
- c.tsw.WriteLiterally("$.box(")
170
+ if needsVarRef {
171
+ // VarRef variable: let v: VarRef<T> = $.varRef(init_or_zero);
172
+ c.tsw.WriteLiterally("$.varRef(")
95
173
  if hasInitializer {
96
174
  // Write the compiled initializer expression normally
97
175
  if err := c.WriteValueExpr(initializerExpr); err != nil {
98
176
  return err
99
177
  }
100
178
  } else {
101
- // No initializer, box the zero value
179
+ // No initializer, varRef the zero value
102
180
  c.WriteZeroValueForType(goType)
103
181
  }
104
182
  c.tsw.WriteLiterally(")")
105
183
  } else {
106
- // Unboxed variable: let v: T = init_or_zero;
184
+ // Unvarrefed variable: let v: T = init_or_zero;
107
185
  if hasInitializer {
108
- // Handle &v initializer specifically for unboxed variables
186
+ // Handle &v initializer specifically for unvarrefed variables
109
187
  if unaryExpr, isUnary := initializerExpr.(*ast.UnaryExpr); isUnary && unaryExpr.Op == token.AND {
110
188
  // Initializer is &v
111
- // Check if v is boxed
112
- needsBoxOperand := false
189
+ // Check if v is varrefed
190
+ needsVarRefOperand := false
113
191
  unaryExprXIdent, ok := unaryExpr.X.(*ast.Ident)
114
192
  if ok {
115
193
  innerObj := c.pkg.TypesInfo.Uses[unaryExprXIdent]
116
- needsBoxOperand = innerObj != nil && c.analysis.NeedsBoxed(innerObj)
194
+ needsVarRefOperand = innerObj != nil && c.analysis.NeedsVarRef(innerObj)
117
195
  }
118
196
 
119
- // If v is boxed, assign the box itself (v)
120
- // If v is not boxed, assign $.box(v)
121
- if needsBoxOperand {
197
+ // If v is varrefed, assign the varRef itself (v)
198
+ // If v is not varrefed, assign $.varRef(v)
199
+ if needsVarRefOperand {
122
200
  // special handling: do not write .value here.
123
201
  c.WriteIdent(unaryExprXIdent, false)
124
202
  } else {
125
- // &unboxedVar -> $.box(unboxedVar)
126
- c.tsw.WriteLiterally("$.box(")
203
+ // &unvarrefedVar -> $.varRef(unvarrefedVar)
204
+ c.tsw.WriteLiterally("$.varRef(")
127
205
  if err := c.WriteValueExpr(unaryExpr.X); err != nil { // Write 'v'
128
206
  return err
129
207
  }
package/compiler/spec.go CHANGED
@@ -78,9 +78,9 @@ func (c *GoToTSCompiler) writeGetterSetter(fieldName string, fieldType types.Typ
78
78
  c.tsw.WriteLine("")
79
79
  }
80
80
 
81
- func (c *GoToTSCompiler) writeBoxedFieldInitializer(fieldName string, fieldType types.Type, isEmbedded bool) {
81
+ func (c *GoToTSCompiler) writeVarRefedFieldInitializer(fieldName string, fieldType types.Type, isEmbedded bool) {
82
82
  c.tsw.WriteLiterally(fieldName)
83
- c.tsw.WriteLiterally(": $.box(")
83
+ c.tsw.WriteLiterally(": $.varRef(")
84
84
 
85
85
  if isEmbedded {
86
86
  if _, isPtr := fieldType.(*types.Pointer); isPtr {
@@ -112,7 +112,7 @@ func (c *GoToTSCompiler) writeBoxedFieldInitializer(fieldName string, fieldType
112
112
 
113
113
  func (c *GoToTSCompiler) writeClonedFieldInitializer(fieldName string, fieldType types.Type, isEmbedded bool) {
114
114
  c.tsw.WriteLiterally(fieldName)
115
- c.tsw.WriteLiterally(": $.box(")
115
+ c.tsw.WriteLiterally(": $.varRef(")
116
116
 
117
117
  if isEmbedded {
118
118
  isPointerToStruct := false
@@ -183,6 +183,12 @@ func (c *GoToTSCompiler) WriteInterfaceTypeSpec(a *ast.TypeSpec, t *ast.Interfac
183
183
  if err := c.WriteValueExpr(a.Name); err != nil {
184
184
  return err
185
185
  }
186
+
187
+ // Write type parameters if present (for generics)
188
+ if a.TypeParams != nil {
189
+ c.WriteTypeParameters(a.TypeParams)
190
+ }
191
+
186
192
  c.tsw.WriteLiterally(" = ")
187
193
  // Get the types.Interface from the ast.InterfaceType.
188
194
  // For an interface definition like `type MyInterface interface { M() }`,
@@ -253,11 +259,20 @@ func (c *GoToTSCompiler) WriteImportSpec(a *ast.ImportSpec) {
253
259
  impName = a.Name.Name
254
260
  }
255
261
 
256
- importPath := translateGoPathToTypescriptPath(goPath)
262
+ // All Go package imports are mapped to the @goscript/ scope.
263
+ // The TypeScript compiler will resolve these using tsconfig paths to either
264
+ // handwritten versions (in .goscript-assets) or transpiled versions (in goscript).
265
+ var tsImportPath string
266
+ if goPath == "github.com/aperturerobotics/goscript/builtin" {
267
+ tsImportPath = "@goscript/builtin/builtin.js"
268
+ } else {
269
+ tsImportPath = "@goscript/" + goPath
270
+ }
271
+
257
272
  c.analysis.Imports[impName] = &fileImport{
258
- importPath: importPath,
273
+ importPath: tsImportPath,
259
274
  importVars: make(map[string]struct{}),
260
275
  }
261
276
 
262
- c.tsw.WriteImport(impName, importPath+"/index.js")
277
+ c.tsw.WriteImport(impName, tsImportPath+"/index.js")
263
278
  }
@@ -5,6 +5,7 @@ import (
5
5
  "go/ast"
6
6
  "go/token"
7
7
  "go/types"
8
+ "strings"
8
9
 
9
10
  "github.com/pkg/errors"
10
11
  )
@@ -32,7 +33,7 @@ import (
32
33
  // - Uses `writeAssignmentCore` which handles:
33
34
  // - Blank identifier `_` on LHS (evaluates RHS for side effects).
34
35
  // - Assignment to dereferenced pointer `*p = val` -> `p_ts!.value = val_ts`.
35
- // - Short declaration `x := y`: `let x = y_ts;`. If `x` is boxed, `let x: $.Box<T> = $.box(y_ts);`.
36
+ // - Short declaration `x := y`: `let x = y_ts;`. If `x` is variable referenced, `let x: $.VarRef<T> = $.varRef(y_ts);`.
36
37
  // - Regular assignment `x = y`, including compound assignments like `x += y`.
37
38
  // - Assignment to map index `m[k] = v` using `$.mapSet`.
38
39
  // - Struct value assignment `s1 = s2` becomes `s1 = s2.clone()` if `s2` is a struct.
@@ -43,7 +44,7 @@ import (
43
44
  // The function ensures that the number of LHS and RHS expressions matches for
44
45
  // most cases, erroring if they don't, except for specifically handled patterns
45
46
  // like multi-assign from single call or discarded channel receive.
46
- // It correctly applies `let` for `:=` (define) tokens and handles boxing and
47
+ // It correctly applies `let` for `:=` (define) tokens and handles varRefing and
47
48
  // cloning semantics based on type information and analysis.
48
49
  func (c *GoToTSCompiler) WriteStmtAssign(exp *ast.AssignStmt) error {
49
50
  // writeMultiVarAssignFromCall handles multi-variable assignment from a single function call.
@@ -130,7 +131,7 @@ func (c *GoToTSCompiler) WriteStmtAssign(exp *ast.AssignStmt) error {
130
131
  // Add type annotation if we have type information
131
132
  if i < len(resultTypes) {
132
133
  c.tsw.WriteLiterally(": ")
133
- c.WriteGoType(resultTypes[i].Type())
134
+ c.WriteGoType(resultTypes[i].Type(), GoTypeContextGeneral)
134
135
  }
135
136
 
136
137
  c.tsw.WriteLine("")
@@ -146,6 +147,10 @@ func (c *GoToTSCompiler) WriteStmtAssign(exp *ast.AssignStmt) error {
146
147
  hasSelectors = true
147
148
  break
148
149
  }
150
+ if _, ok := lhsExpr.(*ast.StarExpr); ok {
151
+ hasSelectors = true
152
+ break
153
+ }
149
154
  }
150
155
 
151
156
  // If we have selector expressions, we need to ensure variables are initialized
@@ -175,6 +180,21 @@ func (c *GoToTSCompiler) WriteStmtAssign(exp *ast.AssignStmt) error {
175
180
  if err := c.WriteValueExpr(selectorExpr); err != nil {
176
181
  return fmt.Errorf("failed to write selector expression in LHS: %w", err)
177
182
  }
183
+ } else if starExpr, ok := lhsExpr.(*ast.StarExpr); ok {
184
+ // Handle pointer dereference assignment: *p = value becomes p!.value = value
185
+ // Write the pointer variable directly without using WriteValueExpr
186
+ // because we don't want automatic .value access here
187
+ switch operand := starExpr.X.(type) {
188
+ case *ast.Ident:
189
+ // Write identifier without .value access
190
+ c.WriteIdent(operand, false)
191
+ default:
192
+ // For other expressions, use WriteValueExpr
193
+ if err := c.WriteValueExpr(starExpr.X); err != nil {
194
+ return fmt.Errorf("failed to write star expression X in LHS: %w", err)
195
+ }
196
+ }
197
+ c.tsw.WriteLiterally("!.value")
178
198
  } else {
179
199
  return errors.Errorf("unhandled LHS expression in assignment: %T", lhsExpr)
180
200
  }
@@ -210,6 +230,21 @@ func (c *GoToTSCompiler) WriteStmtAssign(exp *ast.AssignStmt) error {
210
230
  if err := c.WriteValueExpr(selectorExpr); err != nil {
211
231
  return fmt.Errorf("failed to write selector expression in LHS: %w", err)
212
232
  }
233
+ } else if starExpr, ok := lhsExpr.(*ast.StarExpr); ok {
234
+ // Handle pointer dereference in destructuring: *p becomes p!.value
235
+ // Write the pointer variable directly without using WriteValueExpr
236
+ // because we don't want automatic .value access here
237
+ switch operand := starExpr.X.(type) {
238
+ case *ast.Ident:
239
+ // Write identifier without .value access
240
+ c.WriteIdent(operand, false)
241
+ default:
242
+ // For other expressions, use WriteValueExpr
243
+ if err := c.WriteValueExpr(starExpr.X); err != nil {
244
+ return fmt.Errorf("failed to write star expression X in destructuring: %w", err)
245
+ }
246
+ }
247
+ c.tsw.WriteLiterally("!.value")
213
248
  } else {
214
249
  // Should not happen for valid Go code in this context, but handle defensively
215
250
  return errors.Errorf("unhandled LHS expression in destructuring: %T", lhsExpr)
@@ -392,6 +427,8 @@ func (c *GoToTSCompiler) WriteStmtAssign(exp *ast.AssignStmt) error {
392
427
  if err := c.writeAssignmentCore(exp.Lhs, exp.Rhs, exp.Tok, false); err != nil {
393
428
  return err
394
429
  }
430
+ // Handle potential inline comment for multi-variable assignment
431
+ c.writeInlineComment(exp)
395
432
  c.tsw.WriteLine("") // Add newline after the statement
396
433
  return nil
397
434
  }
@@ -402,6 +439,8 @@ func (c *GoToTSCompiler) WriteStmtAssign(exp *ast.AssignStmt) error {
402
439
  if err := c.writeAssignmentCore(exp.Lhs, exp.Rhs, exp.Tok, addDeclaration); err != nil {
403
440
  return err
404
441
  }
442
+ // Handle potential inline comment for single assignment
443
+ c.writeInlineComment(exp)
405
444
  c.tsw.WriteLine("") // Add newline after the statement
406
445
  return nil
407
446
  }
@@ -409,3 +448,26 @@ func (c *GoToTSCompiler) WriteStmtAssign(exp *ast.AssignStmt) error {
409
448
  // Should not reach here if LHS/RHS counts are valid and handled
410
449
  return fmt.Errorf("unhandled assignment case")
411
450
  }
451
+
452
+ // writeInlineComment checks for and writes any inline comments associated with the given AST node.
453
+ // It is intended to be called immediately after writing the main statement/expression.
454
+ func (c *GoToTSCompiler) writeInlineComment(node ast.Node) {
455
+ if c.pkg == nil || c.pkg.Fset == nil || !node.End().IsValid() {
456
+ return
457
+ }
458
+
459
+ file := c.pkg.Fset.File(node.End())
460
+ if file == nil {
461
+ return
462
+ }
463
+
464
+ endLine := file.Line(node.End())
465
+ // Check comments associated *directly* with the node
466
+ for _, cg := range c.analysis.Cmap[node] {
467
+ if cg.Pos().IsValid() && file.Line(cg.Pos()) == endLine && cg.Pos() > node.End() {
468
+ commentText := strings.TrimSpace(strings.TrimPrefix(cg.Text(), "//"))
469
+ c.tsw.WriteLiterally(" // " + commentText)
470
+ return // Only write the first inline comment found
471
+ }
472
+ }
473
+ }
@@ -105,6 +105,17 @@ func (c *GoToTSCompiler) WriteStmtForInit(stmt ast.Stmt) error {
105
105
  case *ast.ExprStmt:
106
106
  // Handle expression statement in init
107
107
  return c.WriteValueExpr(s.X)
108
+ case *ast.IncDecStmt:
109
+ // Handle increment/decrement in init (e.g., for i++; ...)
110
+ if err := c.WriteValueExpr(s.X); err != nil { // The expression (e.g., i)
111
+ return err
112
+ }
113
+ tokStr, ok := TokenToTs(s.Tok)
114
+ if !ok {
115
+ return errors.Errorf("unknown incdec token: %v", s.Tok)
116
+ }
117
+ c.tsw.WriteLiterally(tokStr) // The token (e.g., ++)
118
+ return nil
108
119
  default:
109
120
  return errors.Errorf("unhandled for loop init statement: %T", stmt)
110
121
  }