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.
- package/README.md +1 -1
- package/cmd/goscript/cmd_compile.go +3 -3
- package/compiler/analysis.go +302 -182
- package/compiler/analysis_test.go +220 -0
- package/compiler/assignment.go +42 -43
- package/compiler/builtin_test.go +102 -0
- package/compiler/compiler.go +117 -29
- package/compiler/compiler_test.go +36 -8
- package/compiler/composite-lit.go +133 -53
- package/compiler/config.go +7 -3
- package/compiler/config_test.go +6 -33
- package/compiler/decl.go +36 -0
- package/compiler/expr-call.go +116 -60
- package/compiler/expr-selector.go +88 -43
- package/compiler/expr-star.go +57 -65
- package/compiler/expr-type.go +132 -5
- package/compiler/expr-value.go +8 -38
- package/compiler/expr.go +326 -30
- package/compiler/field.go +3 -3
- package/compiler/lit.go +34 -2
- package/compiler/primitive.go +19 -12
- package/compiler/spec-struct.go +140 -9
- package/compiler/spec-value.go +119 -41
- package/compiler/spec.go +21 -6
- package/compiler/stmt-assign.go +65 -3
- package/compiler/stmt-for.go +11 -0
- package/compiler/stmt-range.go +119 -11
- package/compiler/stmt-select.go +211 -0
- package/compiler/stmt-type-switch.go +147 -0
- package/compiler/stmt.go +175 -238
- package/compiler/type-assert.go +125 -379
- package/compiler/type.go +216 -129
- package/dist/gs/builtin/builtin.js +37 -0
- package/dist/gs/builtin/builtin.js.map +1 -0
- package/dist/gs/builtin/channel.js +471 -0
- package/dist/gs/builtin/channel.js.map +1 -0
- package/dist/gs/builtin/defer.js +54 -0
- package/dist/gs/builtin/defer.js.map +1 -0
- package/dist/gs/builtin/io.js +15 -0
- package/dist/gs/builtin/io.js.map +1 -0
- package/dist/gs/builtin/map.js +44 -0
- package/dist/gs/builtin/map.js.map +1 -0
- package/dist/gs/builtin/slice.js +799 -0
- package/dist/gs/builtin/slice.js.map +1 -0
- package/dist/gs/builtin/type.js +745 -0
- package/dist/gs/builtin/type.js.map +1 -0
- package/dist/gs/builtin/varRef.js +14 -0
- package/dist/gs/builtin/varRef.js.map +1 -0
- package/dist/gs/context/context.js +55 -0
- package/dist/gs/context/context.js.map +1 -0
- package/dist/gs/context/index.js +2 -0
- package/dist/gs/context/index.js.map +1 -0
- package/dist/gs/runtime/index.js +2 -0
- package/dist/gs/runtime/index.js.map +1 -0
- package/dist/gs/runtime/runtime.js +158 -0
- package/dist/gs/runtime/runtime.js.map +1 -0
- package/dist/gs/time/index.js +2 -0
- package/dist/gs/time/index.js.map +1 -0
- package/dist/gs/time/time.js +115 -0
- package/dist/gs/time/time.js.map +1 -0
- package/package.json +7 -6
- package/builtin/builtin.go +0 -11
- package/builtin/builtin.ts +0 -2379
- package/dist/builtin/builtin.d.ts +0 -513
- package/dist/builtin/builtin.js +0 -1686
- package/dist/builtin/builtin.js.map +0 -1
package/compiler/spec-struct.go
CHANGED
|
@@ -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 `$.
|
|
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: $.
|
|
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.
|
|
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
|
-
|
|
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()",
|
|
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
|
-
|
|
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
|
+
}
|
package/compiler/spec-value.go
CHANGED
|
@@ -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
|
|
16
|
-
// using `c.analysis.
|
|
17
|
-
// - If
|
|
18
|
-
// The type annotation is `$.
|
|
19
|
-
// - If not
|
|
20
|
-
// The type annotation is `T_ts`. If the initializer is `&
|
|
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
|
|
46
|
+
return fmt.Errorf("could not resolve type: %v", name)
|
|
48
47
|
}
|
|
49
48
|
|
|
50
49
|
goType := obj.Type()
|
|
51
|
-
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
//
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
c.tsw.WriteLiterally("
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
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
|
|
93
|
-
//
|
|
94
|
-
c.tsw.WriteLiterally("$.
|
|
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,
|
|
179
|
+
// No initializer, varRef the zero value
|
|
102
180
|
c.WriteZeroValueForType(goType)
|
|
103
181
|
}
|
|
104
182
|
c.tsw.WriteLiterally(")")
|
|
105
183
|
} else {
|
|
106
|
-
//
|
|
184
|
+
// Unvarrefed variable: let v: T = init_or_zero;
|
|
107
185
|
if hasInitializer {
|
|
108
|
-
// Handle &v initializer specifically for
|
|
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
|
|
112
|
-
|
|
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
|
-
|
|
194
|
+
needsVarRefOperand = innerObj != nil && c.analysis.NeedsVarRef(innerObj)
|
|
117
195
|
}
|
|
118
196
|
|
|
119
|
-
// If v is
|
|
120
|
-
// If v is not
|
|
121
|
-
if
|
|
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
|
-
// &
|
|
126
|
-
c.tsw.WriteLiterally("$.
|
|
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)
|
|
81
|
+
func (c *GoToTSCompiler) writeVarRefedFieldInitializer(fieldName string, fieldType types.Type, isEmbedded bool) {
|
|
82
82
|
c.tsw.WriteLiterally(fieldName)
|
|
83
|
-
c.tsw.WriteLiterally(": $.
|
|
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(": $.
|
|
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
|
-
|
|
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:
|
|
273
|
+
importPath: tsImportPath,
|
|
259
274
|
importVars: make(map[string]struct{}),
|
|
260
275
|
}
|
|
261
276
|
|
|
262
|
-
c.tsw.WriteImport(impName,
|
|
277
|
+
c.tsw.WriteImport(impName, tsImportPath+"/index.js")
|
|
263
278
|
}
|
package/compiler/stmt-assign.go
CHANGED
|
@@ -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
|
|
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
|
|
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
|
+
}
|
package/compiler/stmt-for.go
CHANGED
|
@@ -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
|
}
|