goscript 0.0.23 → 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 (53) hide show
  1. package/README.md +1 -1
  2. package/cmd/goscript/cmd_compile.go +1 -1
  3. package/compiler/analysis.go +73 -131
  4. package/compiler/analysis_test.go +220 -0
  5. package/compiler/assignment.go +37 -43
  6. package/compiler/builtin_test.go +102 -0
  7. package/compiler/compiler.go +79 -14
  8. package/compiler/composite-lit.go +108 -43
  9. package/compiler/config.go +7 -3
  10. package/compiler/config_test.go +6 -33
  11. package/compiler/expr-selector.go +66 -41
  12. package/compiler/expr-star.go +57 -65
  13. package/compiler/expr-type.go +1 -1
  14. package/compiler/expr-value.go +1 -1
  15. package/compiler/expr.go +79 -18
  16. package/compiler/primitive.go +11 -10
  17. package/compiler/spec-struct.go +3 -3
  18. package/compiler/spec-value.go +75 -29
  19. package/compiler/spec.go +9 -3
  20. package/compiler/stmt-assign.go +36 -2
  21. package/compiler/stmt-for.go +11 -0
  22. package/compiler/stmt-range.go +110 -0
  23. package/compiler/stmt.go +52 -0
  24. package/compiler/type.go +36 -11
  25. package/dist/gs/builtin/builtin.js +37 -0
  26. package/dist/gs/builtin/builtin.js.map +1 -0
  27. package/dist/gs/builtin/channel.js +471 -0
  28. package/dist/gs/builtin/channel.js.map +1 -0
  29. package/dist/gs/builtin/defer.js +54 -0
  30. package/dist/gs/builtin/defer.js.map +1 -0
  31. package/dist/gs/builtin/io.js +15 -0
  32. package/dist/gs/builtin/io.js.map +1 -0
  33. package/dist/gs/builtin/map.js +44 -0
  34. package/dist/gs/builtin/map.js.map +1 -0
  35. package/dist/gs/builtin/slice.js +799 -0
  36. package/dist/gs/builtin/slice.js.map +1 -0
  37. package/dist/gs/builtin/type.js +745 -0
  38. package/dist/gs/builtin/type.js.map +1 -0
  39. package/dist/gs/builtin/varRef.js +14 -0
  40. package/dist/gs/builtin/varRef.js.map +1 -0
  41. package/dist/gs/context/context.js +55 -0
  42. package/dist/gs/context/context.js.map +1 -0
  43. package/dist/gs/context/index.js +2 -0
  44. package/dist/gs/context/index.js.map +1 -0
  45. package/dist/gs/runtime/index.js +2 -0
  46. package/dist/gs/runtime/index.js.map +1 -0
  47. package/dist/gs/runtime/runtime.js +158 -0
  48. package/dist/gs/runtime/runtime.js.map +1 -0
  49. package/dist/gs/time/index.js +2 -0
  50. package/dist/gs/time/index.js.map +1 -0
  51. package/dist/gs/time/time.js +115 -0
  52. package/dist/gs/time/time.js.map +1 -0
  53. package/package.json +3 -2
@@ -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
  }
@@ -231,5 +231,115 @@ func (c *GoToTSCompiler) WriteStmtRange(exp *ast.RangeStmt) error {
231
231
  }
232
232
  }
233
233
 
234
+ // Handle pointer to array/slice types
235
+ if ptrType, ok := underlying.(*types.Pointer); ok {
236
+ elem := ptrType.Elem().Underlying()
237
+ _, isSlice := elem.(*types.Slice)
238
+ _, isArray := elem.(*types.Array)
239
+ if isArray || isSlice {
240
+ // For pointer to array/slice, we need to dereference the pointer
241
+ // Check if the pointer variable itself is varrefed
242
+
243
+ // Determine the index variable name for the generated loop
244
+ indexVarName := "_i" // Default name
245
+ if exp.Key != nil {
246
+ if keyIdent, ok := exp.Key.(*ast.Ident); ok && keyIdent.Name != "_" {
247
+ indexVarName = keyIdent.Name
248
+ }
249
+ }
250
+ // If both key and value are provided, use an index loop and assign both
251
+ if exp.Key != nil && exp.Value != nil {
252
+ c.tsw.WriteLiterallyf("for (let %s = 0; %s < $.len(", indexVarName, indexVarName)
253
+
254
+ // Write the pointer expression - use WriteIdent to avoid automatic .value access
255
+ // since we'll add !.value for pointer dereference
256
+ if ident, ok := exp.X.(*ast.Ident); ok {
257
+ c.WriteIdent(ident, false) // Don't add .value here
258
+ } else {
259
+ if err := c.WriteValueExpr(exp.X); err != nil {
260
+ return fmt.Errorf("failed to write range loop pointer array/slice expression (key and value): %w", err)
261
+ }
262
+ }
263
+ // Add dereference for the pointer: since we're ranging over a pointer to array/slice,
264
+ // we need to dereference to get to the array/slice
265
+ c.tsw.WriteLiterally("!.value")
266
+ c.tsw.WriteLiterallyf("); %s++) {", indexVarName)
267
+ c.tsw.Indent(1)
268
+ c.tsw.WriteLine("")
269
+ // Declare value if not blank
270
+ if ident, ok := exp.Value.(*ast.Ident); ok && ident.Name != "_" {
271
+ c.tsw.WriteLiterally("const ")
272
+ c.WriteIdent(ident, false)
273
+ c.tsw.WriteLiterally(" = ")
274
+ // Write the pointer expression again for value access
275
+ if identX, ok := exp.X.(*ast.Ident); ok {
276
+ c.WriteIdent(identX, false) // Don't add .value here
277
+ } else {
278
+ if err := c.WriteValueExpr(exp.X); err != nil {
279
+ return fmt.Errorf("failed to write range loop pointer array/slice value expression: %w", err)
280
+ }
281
+ }
282
+ c.tsw.WriteLiterally("!.value![")
283
+ c.tsw.WriteLiterally(indexVarName)
284
+ c.tsw.WriteLiterally("]")
285
+ c.tsw.WriteLine("")
286
+ }
287
+ if err := c.WriteStmt(exp.Body); err != nil {
288
+ return fmt.Errorf("failed to write range loop pointer array/slice body (key and value): %w", err)
289
+ }
290
+ c.tsw.Indent(-1)
291
+ c.tsw.WriteLine("}")
292
+ return nil
293
+ } else if exp.Key != nil && exp.Value == nil { // Only key provided
294
+ c.tsw.WriteLiterallyf("for (let %s = 0; %s < $.len(", indexVarName, indexVarName)
295
+ // Write the pointer expression - use WriteIdent to avoid automatic .value access
296
+ // since we'll add !.value for pointer dereference
297
+ if ident, ok := exp.X.(*ast.Ident); ok {
298
+ c.WriteIdent(ident, false) // Don't add .value here
299
+ } else {
300
+ if err := c.WriteValueExpr(exp.X); err != nil {
301
+ return fmt.Errorf("failed to write expression for the pointer iterable: %w", err)
302
+ }
303
+ }
304
+ c.tsw.WriteLiterally("!.value")
305
+ c.tsw.WriteLiterallyf("); %s++) {", indexVarName)
306
+ c.tsw.Indent(1)
307
+ c.tsw.WriteLine("")
308
+ if err := c.WriteStmtBlock(exp.Body, false); err != nil {
309
+ return fmt.Errorf("failed to write range loop pointer array/slice body (only key): %w", err)
310
+ }
311
+ c.tsw.Indent(-1)
312
+ c.tsw.WriteLine("}")
313
+ return nil
314
+ } else if exp.Key == nil && exp.Value != nil { // Only value provided
315
+ // I think this is impossible. See for_range_value_only test.
316
+ return errors.Errorf("unexpected value without key in for range expression: %v", exp)
317
+ } else {
318
+ // Fallback: simple index loop without declaring range variables, use _i
319
+ indexVarName := "_i"
320
+ c.tsw.WriteLiterallyf("for (let %s = 0; %s < $.len(", indexVarName, indexVarName)
321
+ // Write the pointer expression - use WriteIdent to avoid automatic .value access
322
+ // since we'll add !.value for pointer dereference
323
+ if ident, ok := exp.X.(*ast.Ident); ok {
324
+ c.WriteIdent(ident, false) // Don't add .value here
325
+ } else {
326
+ if err := c.WriteValueExpr(exp.X); err != nil {
327
+ return fmt.Errorf("failed to write range loop pointer array/slice length expression (fallback): %w", err)
328
+ }
329
+ }
330
+ c.tsw.WriteLiterally("!.value")
331
+ c.tsw.WriteLiterallyf("); %s++) {", indexVarName)
332
+ c.tsw.Indent(1)
333
+ c.tsw.WriteLine("")
334
+ if err := c.WriteStmtBlock(exp.Body, false); err != nil {
335
+ return fmt.Errorf("failed to write range loop pointer array/slice body (fallback): %w", err)
336
+ }
337
+ c.tsw.Indent(-1)
338
+ c.tsw.WriteLine("}")
339
+ return nil
340
+ }
341
+ }
342
+ }
343
+
234
344
  return errors.Errorf("unsupported range loop type: %T for expression %v", underlying, exp)
235
345
  }
package/compiler/stmt.go CHANGED
@@ -28,6 +28,8 @@ import (
28
28
  // - Go statements (`ast.GoStmt`): `WriteStmtGo`.
29
29
  // - Select statements (`ast.SelectStmt`): `WriteStmtSelect`.
30
30
  // - Branch statements (`ast.BranchStmt`): `WriteStmtBranch`.
31
+ // - Type switch statements (`ast.TypeSwitchStmt`): `WriteStmtTypeSwitch`.
32
+ // - Labeled statements (`ast.LabeledStmt`): `WriteStmtLabeled`.
31
33
  //
32
34
  // If an unknown statement type is encountered, it returns an error.
33
35
  func (c *GoToTSCompiler) WriteStmt(a ast.Stmt) error {
@@ -109,6 +111,10 @@ func (c *GoToTSCompiler) WriteStmt(a ast.Stmt) error {
109
111
  if err := c.WriteStmtTypeSwitch(exp); err != nil {
110
112
  return fmt.Errorf("failed to write type switch statement: %w", err)
111
113
  }
114
+ case *ast.LabeledStmt:
115
+ if err := c.WriteStmtLabeled(exp); err != nil {
116
+ return fmt.Errorf("failed to write labeled statement: %w", err)
117
+ }
112
118
  default:
113
119
  return errors.Errorf("unknown statement: %#v\n", a)
114
120
  }
@@ -292,6 +298,37 @@ func (c *GoToTSCompiler) WriteStmtGo(exp *ast.GoStmt) error {
292
298
  c.tsw.WriteLiterally(")")
293
299
  c.tsw.WriteLine("")
294
300
 
301
+ c.tsw.Indent(-1)
302
+ c.tsw.WriteLine("})") // Close the queueMicrotask callback and the statement
303
+ case *ast.TypeAssertExpr:
304
+ // Handle type assertion expressions: go x.(func())()
305
+ // We assume this is always synchronous (no async function returned by type assertion)
306
+ c.tsw.WriteLiterally("queueMicrotask(() => {")
307
+
308
+ c.tsw.Indent(1)
309
+ c.tsw.WriteLine("")
310
+
311
+ // Write the type assertion call
312
+ if err := c.WriteTypeAssertExpr(fun); err != nil {
313
+ return fmt.Errorf("failed to write type assertion expression in goroutine: %w", err)
314
+ }
315
+
316
+ // Add non-null assertion since mustTypeAssert throws on failure rather than returning null
317
+ c.tsw.WriteLiterally("!")
318
+
319
+ // Write the function arguments
320
+ c.tsw.WriteLiterally("(")
321
+ for i, arg := range callExpr.Args {
322
+ if i != 0 {
323
+ c.tsw.WriteLiterally(", ")
324
+ }
325
+ if err := c.WriteValueExpr(arg); err != nil {
326
+ return fmt.Errorf("failed to write argument %d in goroutine type assertion function call: %w", i, err)
327
+ }
328
+ }
329
+ c.tsw.WriteLiterally(")")
330
+ c.tsw.WriteLine("")
331
+
295
332
  c.tsw.Indent(-1)
296
333
  c.tsw.WriteLine("})") // Close the queueMicrotask callback and the statement
297
334
  default:
@@ -790,3 +827,18 @@ func (c *GoToTSCompiler) WriteStmtDefer(exp *ast.DeferStmt) error {
790
827
 
791
828
  return nil
792
829
  }
830
+
831
+ // WriteStmtLabeled handles labeled statements (ast.LabeledStmt), such as "label: statement".
832
+ // In TypeScript, this translates to "label: statement" directly.
833
+ func (c *GoToTSCompiler) WriteStmtLabeled(stmt *ast.LabeledStmt) error {
834
+ // Write the label name followed by a colon
835
+ c.tsw.WriteLiterally(stmt.Label.Name)
836
+ c.tsw.WriteLiterally(": ")
837
+
838
+ // Write the labeled statement
839
+ if err := c.WriteStmt(stmt.Stmt); err != nil {
840
+ return fmt.Errorf("failed to write labeled statement: %w", err)
841
+ }
842
+
843
+ return nil
844
+ }
package/compiler/type.go CHANGED
@@ -16,7 +16,7 @@ const (
16
16
  GoTypeContextGeneral GoTypeContext = iota
17
17
  // GoTypeContextFunctionReturn is used when translating types for function return values.
18
18
  // In this context, pointer-to-struct types become `ClassName | null` instead of
19
- // `$.Box<ClassName> | null` because function return values cannot be addressed.
19
+ // `$.VarRef<ClassName> | null` because function return values cannot be addressed.
20
20
  GoTypeContextFunctionReturn
21
21
  )
22
22
 
@@ -27,7 +27,7 @@ const (
27
27
  // The context parameter controls how certain types (especially pointers) are handled:
28
28
  // - GoTypeContextGeneral: Standard type translation
29
29
  // - GoTypeContextFunctionReturn: Special handling for function return types where
30
- // pointer-to-struct types become `ClassName | null` instead of `$.Box<ClassName> | null`
30
+ // pointer-to-struct types become `ClassName | null` instead of `$.VarRef<ClassName> | null`
31
31
  //
32
32
  // It handles nil types as 'any' with a comment, and dispatches to appropriate
33
33
  // type-specific writers for all other recognized Go types.
@@ -47,7 +47,7 @@ func (c *GoToTSCompiler) WriteGoType(typ types.Type, context GoTypeContext) {
47
47
  if context == GoTypeContextFunctionReturn {
48
48
  c.writePointerTypeForFunctionReturn(t)
49
49
  } else {
50
- c.WritePointerType(t)
50
+ c.WritePointerType(t, context)
51
51
  }
52
52
  case *types.Slice:
53
53
  c.WriteSliceType(t)
@@ -78,7 +78,7 @@ func (c *GoToTSCompiler) WriteGoType(typ types.Type, context GoTypeContext) {
78
78
  // writePointerTypeForFunctionReturn translates a Go pointer type (*T) to its TypeScript
79
79
  // equivalent for function return types. Unlike WritePointerType, this function
80
80
  // handles pointer-to-struct types specially: they become `ClassName | null` instead
81
- // of `$.Box<ClassName> | null` because function return values cannot be addressed.
81
+ // of `$.VarRef<ClassName> | null` because function return values cannot be addressed.
82
82
  func (c *GoToTSCompiler) writePointerTypeForFunctionReturn(t *types.Pointer) {
83
83
  elemType := t.Elem()
84
84
 
@@ -93,8 +93,8 @@ func (c *GoToTSCompiler) writePointerTypeForFunctionReturn(t *types.Pointer) {
93
93
  c.WriteGoType(elemType, GoTypeContextFunctionReturn)
94
94
  c.tsw.WriteLiterally(" | null")
95
95
  } else {
96
- // For pointer-to-primitive in function returns, still use boxing
97
- c.tsw.WriteLiterally("$.Box<")
96
+ // For pointer-to-primitive in function returns, still use varRefing
97
+ c.tsw.WriteLiterally("$.VarRef<")
98
98
  c.WriteGoType(elemType, GoTypeContextFunctionReturn)
99
99
  c.tsw.WriteLiterally("> | null")
100
100
  }
@@ -227,11 +227,36 @@ func (c *GoToTSCompiler) WriteNamedType(t *types.Named) {
227
227
  }
228
228
 
229
229
  // WritePointerType translates a Go pointer type (*T) to its TypeScript equivalent.
230
- // It generates $.Box<T_ts> | null, where T_ts is the translated element type.
231
- func (c *GoToTSCompiler) WritePointerType(t *types.Pointer) {
232
- c.tsw.WriteLiterally("$.Box<")
233
- c.WriteGoType(t.Elem(), GoTypeContextGeneral)
234
- c.tsw.WriteLiterally("> | null") // Pointers are always nullable
230
+ func (c *GoToTSCompiler) WritePointerType(t *types.Pointer, context GoTypeContext) {
231
+ elemGoType := t.Elem()
232
+ underlyingElemGoType := elemGoType.Underlying()
233
+
234
+ // Handle pointers to functions: *func(...) -> func(...) | null
235
+ if _, isSignature := underlyingElemGoType.(*types.Signature); isSignature {
236
+ c.WriteGoType(elemGoType, context) // Write the function signature itself, pass context
237
+ c.tsw.WriteLiterally(" | null") // Function pointers are nullable
238
+ return
239
+ }
240
+
241
+ // Handle pointers to structs or interfaces: *MyStruct -> MyStruct | null
242
+ _, isStruct := underlyingElemGoType.(*types.Struct)
243
+ _, isInterface := underlyingElemGoType.(*types.Interface)
244
+
245
+ if isStruct || isInterface {
246
+ // For pointers to structs or interfaces, the TS type is StructName | null or InterfaceName | null.
247
+ // This aligns with VAR_REFS.md and JS/TS object reference semantics.
248
+ // TODO If the target variable is boxed, we have to wrap with VarRef as well?
249
+ c.WriteGoType(elemGoType, context) // Write the struct/interface type directly, pass context
250
+ c.tsw.WriteLiterally(" | null")
251
+ } else {
252
+ // For pointers to other types (primitives, slices, maps, other pointers like **MyStruct),
253
+ // they are generally represented as $.VarRef<T_ts> | null.
254
+ // Example: *int -> $.VarRef<number> | null
255
+ // Example: **MyStruct -> $.VarRef<MyStruct | null> | null (recursive call handles inner part)
256
+ c.tsw.WriteLiterally("$.VarRef<")
257
+ c.WriteGoType(elemGoType, context) // Translate element type, pass context
258
+ c.tsw.WriteLiterally("> | null") // Pointers are always nullable
259
+ }
235
260
  }
236
261
 
237
262
  // WriteSliceType translates a Go slice type ([]T) to its TypeScript equivalent.
@@ -0,0 +1,37 @@
1
+ export * from './varRef.js';
2
+ export * from './channel.js';
3
+ export * from './defer.js';
4
+ export * from './io.js';
5
+ export * from './map.js';
6
+ export * from './slice.js';
7
+ export * from './type.js';
8
+ // Duration multiplication helper for time package operations
9
+ // Handles expressions like time.Hour * 24
10
+ export function multiplyDuration(duration, multiplier) {
11
+ // Check if duration has a multiply method (like our Duration class)
12
+ if (duration && typeof duration.multiply === 'function') {
13
+ return duration.multiply(multiplier);
14
+ }
15
+ // Check if duration has a valueOf method for numeric operations
16
+ if (duration && typeof duration.valueOf === 'function') {
17
+ const numValue = duration.valueOf();
18
+ // Return an object with the same structure but multiplied value
19
+ if (typeof numValue === 'number') {
20
+ // Try to create a new instance of the same type
21
+ if (duration.constructor) {
22
+ return new duration.constructor(numValue * multiplier);
23
+ }
24
+ // Fallback: return a simple object with valueOf
25
+ return {
26
+ valueOf: () => numValue * multiplier,
27
+ toString: () => (numValue * multiplier).toString() + "ns"
28
+ };
29
+ }
30
+ }
31
+ // Fallback for simple numeric values
32
+ if (typeof duration === 'number') {
33
+ return duration * multiplier;
34
+ }
35
+ throw new Error(`Cannot multiply duration of type ${typeof duration}`);
36
+ }
37
+ //# sourceMappingURL=builtin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builtin.js","sourceRoot":"","sources":["../../../gs/builtin/builtin.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AAEzB,6DAA6D;AAC7D,0CAA0C;AAC1C,MAAM,UAAU,gBAAgB,CAAC,QAAa,EAAE,UAAkB,EAAO;IACvE,oEAAoE;IACpE,IAAI,QAAQ,IAAI,OAAO,QAAQ,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC;QACxD,OAAO,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IACvC,CAAC;IAED,gEAAgE;IAChE,IAAI,QAAQ,IAAI,OAAO,QAAQ,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;QACvD,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC;QACpC,gEAAgE;QAChE,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACjC,gDAAgD;YAChD,IAAI,QAAQ,CAAC,WAAW,EAAE,CAAC;gBACzB,OAAO,IAAI,QAAQ,CAAC,WAAW,CAAC,QAAQ,GAAG,UAAU,CAAC,CAAC;YACzD,CAAC;YACD,gDAAgD;YAChD,OAAO;gBACL,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,GAAG,UAAU;gBACpC,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,QAAQ,GAAG,UAAU,CAAC,CAAC,QAAQ,EAAE,GAAG,IAAI;aAC1D,CAAC;QACJ,CAAC;IACH,CAAC;IAED,qCAAqC;IACrC,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,QAAQ,GAAG,UAAU,CAAC;IAC/B,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,oCAAoC,OAAO,QAAQ,EAAE,CAAC,CAAC;AAAA,CACxE"}