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
@@ -1,89 +1,81 @@
1
1
  package compiler
2
2
 
3
3
  import (
4
- "fmt"
5
4
  "go/ast"
5
+ "go/types"
6
6
  )
7
7
 
8
8
  // WriteStarExpr translates a Go pointer dereference expression (`ast.StarExpr`, e.g., `*p`)
9
9
  // into its TypeScript equivalent. This involves careful handling of Go's pointers
10
- // and TypeScript's boxing mechanism for emulating pointer semantics.
10
+ // and TypeScript's varRefing mechanism for emulating pointer semantics.
11
11
  //
12
- // The translation depends on whether the pointer variable `p` itself is boxed and
12
+ // The translation depends on whether the pointer variable `p` itself is varrefed and
13
13
  // what type of value it points to:
14
- // 1. If `p` is not boxed and points to a primitive or another pointer: `*p` -> `p!.value`.
15
- // (`p` holds a box, so dereference accesses its `value` field).
16
- // 2. If `p` is not boxed and points to a struct: `*p` -> `p!`.
14
+ // 1. If `p` is not varrefed and points to a primitive or another pointer: `*p` -> `p!.value`.
15
+ // (`p` holds a varRef, so dereference accesses its `value` field).
16
+ // 2. If `p` is not varrefed and points to a struct: `*p` -> `p!`.
17
17
  // (`p` holds the struct instance directly; structs are reference types in TS).
18
- // 3. If `p` is boxed (i.e., `p` is `$.Box<PointerType>`) and points to a primitive/pointer:
19
- // `*p` -> `p.value!.value`.
20
- // (First `.value` unboxes `p`, then `!.value` dereferences the inner pointer).
21
- // 4. If `p` is boxed and points to a struct: `*p` -> `p.value!`.
22
- // (First `.value` unboxes `p` to get the struct instance).
18
+ // 3. If `p` is variable referenced (i.e., `p` is `$.VarRef<PointerType>`) and points to a primitive/pointer:
19
+ // `p.value!.value` (access the variable reference, then dereference the pointer)
20
+ // 4. If `p` is varrefed and points to a struct: `p.value!`.
21
+ // (First `.value` unvarRefes `p` to get the struct instance).
23
22
  //
24
- // `WriteValueExpr(operand)` handles the initial unboxing of `p` if `p` itself is a boxed variable.
23
+ // `WriteValueExpr(operand)` handles the initial unvarRefing of `p` if `p` itself is a varrefed variable.
25
24
  // A non-null assertion `!` is always added as pointers can be nil.
26
- // `c.analysis.NeedsBoxedDeref(ptrType)` determines if an additional `.value` is needed
27
- // based on whether the dereferenced type is a primitive/pointer (requires `.value`) or
28
- // a struct (does not require `.value`).
25
+ // The function determines if `.value` access is needed by checking what the Go pointer operand points to.
26
+ //
27
+ // For multi-level dereferences like `***p`, this function is called recursively, with each level
28
+ // adding the appropriate `!.value` suffix.
29
+ //
30
+ // Examples:
31
+ // - Simple pointer to primitive: `p!.value` (where p is *int)
32
+ // - Variable referenced pointer to primitive: `p.value!.value` (where p is VarRef<*int>)
33
+ // Example: let p = $.varRef(x) (where x is another variable reference) => p.value!.value
34
+ // - Pointer to struct: `p!` (where p is *MyStruct)
35
+ // Example: let p = $.varRef(new MyStruct()) => p.value!
36
+ // - Variable referenced pointer to struct: `p.value!` (where p is VarRef<*MyStruct>)
37
+ // - Triple pointer: `p3!.value!.value!.value` (where p3 is VarRef<VarRef<VarRef<number> | null> | null> | null)
29
38
  func (c *GoToTSCompiler) WriteStarExpr(exp *ast.StarExpr) error {
30
- // Generate code for a pointer dereference expression (*p).
31
- //
32
- // IMPORTANT: Pointer dereferencing in TypeScript requires careful handling of the box/unbox state:
33
- //
34
- // 1. p!.value - when p is not boxed and points to a primitive/pointer
35
- // Example: let p = x (where x is a box) => p!.value
36
- //
37
- // 2. p! - when p is not boxed and points to a struct
38
- // Example: let p = new MyStruct() => p! (structs are reference types)
39
- //
40
- // 3. p.value!.value - when p is boxed and points to a primitive/pointer
41
- // Example: let p = $.box(x) (where x is another box) => p.value!.value
42
- //
43
- // 4. p.value! - when p is boxed and points to a struct
44
- // Example: let p = $.box(new MyStruct()) => p.value!
45
- //
46
- // Critical bug fix: We must handle each case correctly to avoid over-dereferencing
47
- // (adding too many .value) or under-dereferencing (missing .value where needed)
48
- //
49
- // NOTE: This logic aligns with design/BOXES_POINTERS.md.
50
-
51
- // Get the operand expression and its type information
52
- operand := exp.X
53
-
54
- // Get the type of the operand (the pointer being dereferenced)
55
- ptrType := c.pkg.TypesInfo.TypeOf(operand)
56
-
57
- // Special case for handling multi-level dereferencing:
58
- // Check if the operand is itself a StarExpr (e.g., **p or ***p)
59
- // We need to handle these specially to correctly generate nested .value accesses
60
- if starExpr, isStarExpr := operand.(*ast.StarExpr); isStarExpr {
61
- // First, write the inner star expression
62
- if err := c.WriteStarExpr(starExpr); err != nil {
63
- return fmt.Errorf("failed to write inner star expression: %w", err)
39
+ // Check if the operand is an identifier that is varrefed
40
+ isVarrefedIdent := false
41
+ if ident, ok := exp.X.(*ast.Ident); ok {
42
+ if obj := c.pkg.TypesInfo.ObjectOf(ident); obj != nil {
43
+ isVarrefedIdent = c.analysis.NeedsVarRef(obj)
64
44
  }
65
-
66
- // Always add .value for multi-level dereferences
67
- // For expressions like **p, each * adds a .value
68
- c.tsw.WriteLiterally("!.value")
69
- return nil
70
45
  }
71
46
 
72
- // Standard case: single-level dereference
73
- // Write the pointer expression, which will access .value if the variable is boxed
74
- // WriteValueExpr will add .value if the variable itself is boxed (p.value)
75
- if err := c.WriteValueExpr(operand); err != nil {
76
- return fmt.Errorf("failed to write star expression operand: %w", err)
47
+ // Write the operand
48
+ if isVarrefedIdent {
49
+ // For varrefed identifiers, we need to access the value first
50
+ if err := c.WriteValueExpr(exp.X); err != nil {
51
+ return err
52
+ }
53
+ } else {
54
+ // For non-varrefed identifiers and other expressions
55
+ switch operand := exp.X.(type) {
56
+ case *ast.Ident:
57
+ // Write identifier without .value access
58
+ c.WriteIdent(operand, false)
59
+ default:
60
+ // For other expressions (like nested star expressions), use WriteValueExpr
61
+ if err := c.WriteValueExpr(exp.X); err != nil {
62
+ return err
63
+ }
64
+ }
77
65
  }
78
66
 
79
- // Add ! for null assertion - all pointers can be null in TypeScript
67
+ // Add non-null assertion for pointer safety
80
68
  c.tsw.WriteLiterally("!")
81
69
 
82
- // Add .value only if we need boxed dereferencing for this type of pointer
83
- // This depends on whether we're dereferencing to a primitive (needs .value)
84
- // or to a struct (no .value needed)
85
- if c.analysis.NeedsBoxedDeref(ptrType) {
86
- c.tsw.WriteLiterally(".value")
70
+ // Check what the operand points to (not what the result is)
71
+ operandType := c.pkg.TypesInfo.TypeOf(exp.X)
72
+ if ptrType, isPtr := operandType.(*types.Pointer); isPtr {
73
+ elemType := ptrType.Elem()
74
+ // Only add .value if NOT pointing to a struct
75
+ if _, isStruct := elemType.Underlying().(*types.Struct); !isStruct {
76
+ c.tsw.WriteLiterally(".value")
77
+ }
78
+ // If pointing to a struct, don't add .value (structs are reference types in TS)
87
79
  }
88
80
 
89
81
  return nil
@@ -11,7 +11,7 @@ import (
11
11
  // It handles various Go type expressions:
12
12
  // - Basic types (e.g., int, string, bool) -> TypeScript primitives (number, string, boolean)
13
13
  // - Named types -> TypeScript class/interface names
14
- // - Pointer types (`*T`) -> `$.Box<T_ts> | null`
14
+ // - Pointer types (`*T`) -> `$.VarRef<T_ts> | null`
15
15
  // - Slice types (`[]T`) -> `$.Slice<T_ts>`
16
16
  // - Array types (`[N]T`) -> `T_ts[]`
17
17
  // - Map types (`map[K]V`) -> `Map<K_ts, V_ts>`
@@ -22,13 +22,59 @@ import (
22
22
  func (c *GoToTSCompiler) WriteTypeExpr(a ast.Expr) {
23
23
  // Get type information for the expression and use WriteGoType
24
24
  typ := c.pkg.TypesInfo.TypeOf(a)
25
- c.WriteGoType(typ)
25
+ c.WriteGoType(typ, GoTypeContextGeneral)
26
26
  }
27
27
 
28
28
  // writeTypeDescription writes the TypeInfo for a type expr.
29
29
  func (c *GoToTSCompiler) writeTypeDescription(typeExpr ast.Expr) {
30
30
  switch t := typeExpr.(type) {
31
31
  case *ast.Ident:
32
+ // Resolve the identifier to its type
33
+ goType := c.pkg.TypesInfo.TypeOf(t)
34
+ if goType != nil {
35
+ if namedType, isNamed := goType.(*types.Named); isNamed {
36
+ if sig, isFuncSig := namedType.Underlying().(*types.Signature); isFuncSig {
37
+ // It's a named function type (e.g. type MyFunc func())
38
+ c.tsw.WriteLiterally("{")
39
+ c.tsw.WriteLiterally("kind: $.TypeKind.Function, ")
40
+ c.tsw.WriteLiterallyf("name: '%s'", namedType.Obj().Name()) // Use the original defined name
41
+
42
+ // Add params if present
43
+ if sig.Params() != nil && sig.Params().Len() > 0 {
44
+ c.tsw.WriteLiterally(", params: [")
45
+ for i := 0; i < sig.Params().Len(); i++ {
46
+ if i > 0 {
47
+ c.tsw.WriteLiterally(", ")
48
+ }
49
+ paramVar := sig.Params().At(i)
50
+ c.writeTypeInfoObject(paramVar.Type())
51
+ }
52
+ c.tsw.WriteLiterally("]")
53
+ } else {
54
+ c.tsw.WriteLiterally(", params: []")
55
+ }
56
+
57
+ // Add results if present
58
+ if sig.Results() != nil && sig.Results().Len() > 0 {
59
+ c.tsw.WriteLiterally(", results: [")
60
+ for i := 0; i < sig.Results().Len(); i++ {
61
+ if i > 0 {
62
+ c.tsw.WriteLiterally(", ")
63
+ }
64
+ resultVar := sig.Results().At(i)
65
+ c.writeTypeInfoObject(resultVar.Type())
66
+ }
67
+ c.tsw.WriteLiterally("]")
68
+ } else {
69
+ c.tsw.WriteLiterally(", results: []")
70
+ }
71
+
72
+ c.tsw.WriteLiterally("}")
73
+ return
74
+ }
75
+ }
76
+ }
77
+
32
78
  if isPrimitiveType(t.Name) {
33
79
  if tsType, ok := GoBuiltinToTypescript(t.Name); ok {
34
80
  c.tsw.WriteLiterally("{")
@@ -112,9 +158,16 @@ func (c *GoToTSCompiler) writeTypeDescription(typeExpr ast.Expr) {
112
158
  c.tsw.WriteLiterally("kind: $.TypeKind.Function")
113
159
 
114
160
  // Add name if this is a named function type
115
- if namedType := c.pkg.TypesInfo.TypeOf(typeExpr); namedType != nil {
116
- if named, ok := namedType.(*types.Named); ok {
117
- c.tsw.WriteLiterallyf(", name: '%s'", named.Obj().Name())
161
+ // For an anonymous ast.FuncType, typeExpr is the ast.FuncType itself.
162
+ // We need to check if the type information associated with this AST node
163
+ // points to a *types.Named type.
164
+ resolvedGoType := c.pkg.TypesInfo.TypeOf(typeExpr)
165
+ if resolvedGoType != nil {
166
+ if named, ok := resolvedGoType.(*types.Named); ok {
167
+ // Ensure it's actually a function type that's named
168
+ if _, isFuncSig := named.Underlying().(*types.Signature); isFuncSig {
169
+ c.tsw.WriteLiterallyf(", name: '%s'", named.Obj().Name())
170
+ }
118
171
  }
119
172
  }
120
173
 
@@ -174,6 +227,80 @@ func (c *GoToTSCompiler) writeTypeDescription(typeExpr ast.Expr) {
174
227
  c.tsw.WriteLiterally("'both'")
175
228
  }
176
229
 
230
+ c.tsw.WriteLiterally("}")
231
+ case *ast.InterfaceType:
232
+ // Handle inline interface types like interface{ Method() string }
233
+ c.tsw.WriteLiterally("{")
234
+ c.tsw.WriteLiterally("kind: $.TypeKind.Interface, ")
235
+ c.tsw.WriteLiterally("methods: [")
236
+
237
+ // Add method signatures for each method in the interface
238
+ if t.Methods != nil && t.Methods.List != nil {
239
+ hasMethod := false
240
+ for _, field := range t.Methods.List {
241
+ // Only process method declarations (not embedded interfaces)
242
+ if len(field.Names) > 0 {
243
+ for _, methodName := range field.Names {
244
+ if hasMethod {
245
+ c.tsw.WriteLiterally(", ")
246
+ }
247
+ hasMethod = true
248
+
249
+ // Write method signature in the same format as writeMethodSignatures
250
+ c.tsw.WriteLiterallyf("{ name: '%s', args: [", methodName.Name)
251
+
252
+ // Get the function type for this method
253
+ if funcType, ok := field.Type.(*ast.FuncType); ok {
254
+ // Add parameters
255
+ if funcType.Params != nil && funcType.Params.List != nil {
256
+ paramIndex := 0
257
+ for _, param := range funcType.Params.List {
258
+ // Each parameter field can declare multiple variables of the same type
259
+ if len(param.Names) > 0 {
260
+ for _, paramName := range param.Names {
261
+ if paramIndex > 0 {
262
+ c.tsw.WriteLiterally(", ")
263
+ }
264
+ c.tsw.WriteLiterallyf("{ name: '%s', type: ", paramName.Name)
265
+ c.writeTypeDescription(param.Type)
266
+ c.tsw.WriteLiterally(" }")
267
+ paramIndex++
268
+ }
269
+ } else {
270
+ // No names, create a generic parameter name
271
+ if paramIndex > 0 {
272
+ c.tsw.WriteLiterally(", ")
273
+ }
274
+ c.tsw.WriteLiterallyf("{ name: '_p%d', type: ", paramIndex)
275
+ c.writeTypeDescription(param.Type)
276
+ c.tsw.WriteLiterally(" }")
277
+ paramIndex++
278
+ }
279
+ }
280
+ }
281
+
282
+ c.tsw.WriteLiterally("], returns: [")
283
+
284
+ // Add return types
285
+ if funcType.Results != nil && funcType.Results.List != nil {
286
+ for i, result := range funcType.Results.List {
287
+ if i > 0 {
288
+ c.tsw.WriteLiterally(", ")
289
+ }
290
+ c.tsw.WriteLiterally("{ type: ")
291
+ c.writeTypeDescription(result.Type)
292
+ c.tsw.WriteLiterally(" }")
293
+ }
294
+ }
295
+
296
+ c.tsw.WriteLiterally("] }")
297
+ }
298
+ }
299
+ }
300
+ }
301
+ }
302
+
303
+ c.tsw.WriteLiterally("]")
177
304
  c.tsw.WriteLiterally("}")
178
305
  default:
179
306
  // For other types, use the string representation
@@ -1,11 +1,13 @@
1
1
  package compiler
2
2
 
3
- import "go/ast"
3
+ import (
4
+ "go/ast"
5
+ )
4
6
 
5
7
  // WriteValueExpr translates a Go abstract syntax tree (AST) expression (`ast.Expr`)
6
8
  // that represents a value into its TypeScript value equivalent.
7
9
  // This is a central dispatch function for various expression types:
8
- // - Identifiers (`ast.Ident`): Delegates to `WriteIdent`, potentially adding `.value` for boxed variables.
10
+ // - Identifiers (`ast.Ident`): Delegates to `WriteIdent`, potentially adding `.value` for varrefed variables.
9
11
  // - Selector expressions (`ast.SelectorExpr`, e.g., `obj.Field` or `pkg.Var`): Delegates to `WriteSelectorExpr`.
10
12
  // - Pointer dereferences (`ast.StarExpr`, e.g., `*ptr`): Delegates to `WriteStarExpr`.
11
13
  // - Function calls (`ast.CallExpr`): Delegates to `WriteCallExpr`.
@@ -50,43 +52,11 @@ func (c *GoToTSCompiler) WriteValueExpr(a ast.Expr) error {
50
52
  return c.WriteTypeAssertExpr(exp)
51
53
  case *ast.IndexExpr:
52
54
  return c.WriteIndexExpr(exp)
55
+ case *ast.IndexListExpr:
56
+ // Handle generic function instantiation: f[T1, T2](args) -> f<T1, T2>(args)
57
+ return c.WriteIndexListExpr(exp)
53
58
  case *ast.SliceExpr:
54
- // Translate Go slice expression to $.goSlice(x, low, high, max)
55
- c.tsw.WriteLiterally("$.goSlice(")
56
- if err := c.WriteValueExpr(exp.X); err != nil {
57
- return err
58
- }
59
- // low argument
60
- c.tsw.WriteLiterally(", ")
61
- if exp.Low != nil {
62
- if err := c.WriteValueExpr(exp.Low); err != nil {
63
- return err
64
- }
65
- } else {
66
- c.tsw.WriteLiterally("undefined")
67
- }
68
- // high argument
69
- c.tsw.WriteLiterally(", ")
70
- if exp.High != nil {
71
- if err := c.WriteValueExpr(exp.High); err != nil {
72
- return err
73
- }
74
- } else {
75
- c.tsw.WriteLiterally("undefined")
76
- }
77
- // max argument (only for full slice expressions)
78
- if exp.Slice3 {
79
- c.tsw.WriteLiterally(", ")
80
- if exp.Max != nil {
81
- if err := c.WriteValueExpr(exp.Max); err != nil {
82
- return err
83
- }
84
- } else {
85
- c.tsw.WriteLiterally("undefined")
86
- }
87
- }
88
- c.tsw.WriteLiterally(")")
89
- return nil
59
+ return c.WriteSliceExpr(exp)
90
60
  case *ast.ParenExpr:
91
61
  // Check if this is a nil pointer to struct type cast: (*struct{})(nil)
92
62
  if starExpr, isStarExpr := exp.X.(*ast.StarExpr); isStarExpr {