goscript 0.0.21 → 0.0.23
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/cmd/goscript/cmd_compile.go +2 -2
- package/compiler/analysis.go +229 -51
- package/compiler/assignment.go +412 -0
- package/compiler/compiler.go +185 -5885
- package/compiler/compiler_test.go +40 -8
- package/compiler/composite-lit.go +552 -0
- package/compiler/config.go +3 -0
- package/compiler/decl.go +259 -0
- package/compiler/expr-call.go +479 -0
- package/compiler/expr-selector.go +125 -0
- package/compiler/expr-star.go +90 -0
- package/compiler/expr-type.go +309 -0
- package/compiler/expr-value.go +89 -0
- package/compiler/expr.go +591 -0
- package/compiler/field.go +169 -0
- package/compiler/lit.go +131 -0
- package/compiler/primitive.go +148 -0
- package/compiler/{write-type-spec.go → spec-struct.go} +211 -204
- package/compiler/spec-value.go +226 -0
- package/compiler/spec.go +272 -0
- package/compiler/stmt-assign.go +439 -0
- package/compiler/stmt-for.go +178 -0
- package/compiler/stmt-range.go +235 -0
- package/compiler/stmt-select.go +211 -0
- package/compiler/stmt-type-switch.go +147 -0
- package/compiler/stmt.go +792 -0
- package/compiler/type-assert.go +209 -0
- package/compiler/type-info.go +141 -0
- package/compiler/type.go +618 -0
- package/go.mod +2 -1
- package/go.sum +4 -2
- package/package.json +6 -6
- package/builtin/builtin.go +0 -11
- package/builtin/builtin.ts +0 -2114
- package/dist/builtin/builtin.d.ts +0 -495
- package/dist/builtin/builtin.js +0 -1490
- package/dist/builtin/builtin.js.map +0 -1
- /package/compiler/{writer.go → code-writer.go} +0 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"go/ast"
|
|
6
|
+
"go/token"
|
|
7
|
+
"go/types"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// WriteValueSpec translates a Go value specification (`ast.ValueSpec`),
|
|
11
|
+
// which represents `var` or `const` declarations, into TypeScript `let`
|
|
12
|
+
// declarations.
|
|
13
|
+
//
|
|
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.
|
|
22
|
+
// - If no initializer is provided, the TypeScript zero value (from `WriteZeroValueForType`)
|
|
23
|
+
// is used.
|
|
24
|
+
// - Type `T` (or `T_ts`) is obtained from `obj.Type()` and translated via `WriteGoType`.
|
|
25
|
+
//
|
|
26
|
+
// For multiple variable declarations (`var a, b = val1, val2` or `a, b := val1, val2`):
|
|
27
|
+
// - It uses TypeScript array destructuring: `let [a, b] = [val1_ts, val2_ts];`.
|
|
28
|
+
// - If initialized from a single multi-return function call (`a, b := func()`),
|
|
29
|
+
// it becomes `let [a, b] = func_ts();`.
|
|
30
|
+
// - If no initializers are provided, it defaults to `let [a,b] = []` (with a TODO
|
|
31
|
+
// to assign correct individual zero values).
|
|
32
|
+
//
|
|
33
|
+
// Documentation comments associated with the `ValueSpec` are preserved.
|
|
34
|
+
func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
|
|
35
|
+
if a.Doc != nil {
|
|
36
|
+
c.WriteDoc(a.Doc)
|
|
37
|
+
}
|
|
38
|
+
if a.Comment != nil {
|
|
39
|
+
c.WriteDoc(a.Comment)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Handle single variable declaration
|
|
43
|
+
if len(a.Names) == 1 {
|
|
44
|
+
name := a.Names[0]
|
|
45
|
+
obj := c.pkg.TypesInfo.Defs[name]
|
|
46
|
+
if obj == nil {
|
|
47
|
+
return fmt.Errorf("could not resolve type: %v", name)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
goType := obj.Type()
|
|
51
|
+
needsBox := c.analysis.NeedsBoxed(obj) // Check if address is taken
|
|
52
|
+
|
|
53
|
+
hasInitializer := len(a.Values) > 0
|
|
54
|
+
var initializerExpr ast.Expr
|
|
55
|
+
if hasInitializer {
|
|
56
|
+
initializerExpr = a.Values[0]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Check if the initializer will result in an $.arrayToSlice call in TypeScript
|
|
60
|
+
isSliceConversion := false
|
|
61
|
+
if hasInitializer {
|
|
62
|
+
// Case 1: Direct call to $.arrayToSlice in Go source (less common for typical array literals)
|
|
63
|
+
if callExpr, isCallExpr := initializerExpr.(*ast.CallExpr); isCallExpr {
|
|
64
|
+
if selExpr, isSelExpr := callExpr.Fun.(*ast.SelectorExpr); isSelExpr {
|
|
65
|
+
if pkgIdent, isPkgIdent := selExpr.X.(*ast.Ident); isPkgIdent && pkgIdent.Name == "$" {
|
|
66
|
+
if selExpr.Sel.Name == "arrayToSlice" {
|
|
67
|
+
isSliceConversion = true
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Case 2: Go array or slice literal, which will be compiled to $.arrayToSlice
|
|
74
|
+
// We also check if the original Go type is actually a slice or array.
|
|
75
|
+
if !isSliceConversion { // Only check if not already determined by Case 1
|
|
76
|
+
if _, isCompositeLit := initializerExpr.(*ast.CompositeLit); isCompositeLit {
|
|
77
|
+
switch goType.Underlying().(type) {
|
|
78
|
+
case *types.Slice, *types.Array:
|
|
79
|
+
isSliceConversion = true
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Start declaration
|
|
86
|
+
c.tsw.WriteLiterally("let ")
|
|
87
|
+
c.tsw.WriteLiterally(name.Name)
|
|
88
|
+
|
|
89
|
+
if !isSliceConversion {
|
|
90
|
+
c.tsw.WriteLiterally(": ")
|
|
91
|
+
// Write type annotation
|
|
92
|
+
if needsBox {
|
|
93
|
+
// If boxed, the variable holds Box<OriginalGoType>
|
|
94
|
+
c.tsw.WriteLiterally("$.Box<")
|
|
95
|
+
c.WriteGoType(goType, GoTypeContextGeneral) // Write the original Go type T
|
|
96
|
+
c.tsw.WriteLiterally(">")
|
|
97
|
+
} else {
|
|
98
|
+
// If not boxed, the variable holds the translated Go type directly
|
|
99
|
+
c.WriteGoType(goType, GoTypeContextGeneral)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Write initializer
|
|
104
|
+
c.tsw.WriteLiterally(" = ")
|
|
105
|
+
|
|
106
|
+
// Special case for nil pointer to struct type: (*struct{})(nil)
|
|
107
|
+
if hasInitializer {
|
|
108
|
+
if callExpr, isCallExpr := initializerExpr.(*ast.CallExpr); isCallExpr {
|
|
109
|
+
if starExpr, isStarExpr := callExpr.Fun.(*ast.StarExpr); isStarExpr {
|
|
110
|
+
if _, isStructType := starExpr.X.(*ast.StructType); isStructType {
|
|
111
|
+
// Check if the argument is nil
|
|
112
|
+
if len(callExpr.Args) == 1 {
|
|
113
|
+
if nilIdent, isIdent := callExpr.Args[0].(*ast.Ident); isIdent && nilIdent.Name == "nil" {
|
|
114
|
+
c.tsw.WriteLiterally("null")
|
|
115
|
+
c.tsw.WriteLine("") // Ensure newline after null
|
|
116
|
+
return nil
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if needsBox {
|
|
125
|
+
// Boxed variable: let v: Box<T> = $.box(init_or_zero);
|
|
126
|
+
c.tsw.WriteLiterally("$.box(")
|
|
127
|
+
if hasInitializer {
|
|
128
|
+
// Write the compiled initializer expression normally
|
|
129
|
+
if err := c.WriteValueExpr(initializerExpr); err != nil {
|
|
130
|
+
return err
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
// No initializer, box the zero value
|
|
134
|
+
c.WriteZeroValueForType(goType)
|
|
135
|
+
}
|
|
136
|
+
c.tsw.WriteLiterally(")")
|
|
137
|
+
} else {
|
|
138
|
+
// Unboxed variable: let v: T = init_or_zero;
|
|
139
|
+
if hasInitializer {
|
|
140
|
+
// Handle &v initializer specifically for unboxed variables
|
|
141
|
+
if unaryExpr, isUnary := initializerExpr.(*ast.UnaryExpr); isUnary && unaryExpr.Op == token.AND {
|
|
142
|
+
// Initializer is &v
|
|
143
|
+
// Check if v is boxed
|
|
144
|
+
needsBoxOperand := false
|
|
145
|
+
unaryExprXIdent, ok := unaryExpr.X.(*ast.Ident)
|
|
146
|
+
if ok {
|
|
147
|
+
innerObj := c.pkg.TypesInfo.Uses[unaryExprXIdent]
|
|
148
|
+
needsBoxOperand = innerObj != nil && c.analysis.NeedsBoxed(innerObj)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// If v is boxed, assign the box itself (v)
|
|
152
|
+
// If v is not boxed, assign $.box(v)
|
|
153
|
+
if needsBoxOperand {
|
|
154
|
+
// special handling: do not write .value here.
|
|
155
|
+
c.WriteIdent(unaryExprXIdent, false)
|
|
156
|
+
} else {
|
|
157
|
+
// &unboxedVar -> $.box(unboxedVar)
|
|
158
|
+
c.tsw.WriteLiterally("$.box(")
|
|
159
|
+
if err := c.WriteValueExpr(unaryExpr.X); err != nil { // Write 'v'
|
|
160
|
+
return err
|
|
161
|
+
}
|
|
162
|
+
c.tsw.WriteLiterally(")")
|
|
163
|
+
}
|
|
164
|
+
} else {
|
|
165
|
+
// Regular initializer, clone if needed
|
|
166
|
+
if shouldApplyClone(c.pkg, initializerExpr) {
|
|
167
|
+
if err := c.WriteValueExpr(initializerExpr); err != nil {
|
|
168
|
+
return err
|
|
169
|
+
}
|
|
170
|
+
c.tsw.WriteLiterally(".clone()")
|
|
171
|
+
} else {
|
|
172
|
+
if err := c.WriteValueExpr(initializerExpr); err != nil {
|
|
173
|
+
return err
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
// No initializer, use the zero value directly
|
|
179
|
+
c.WriteZeroValueForType(goType)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
c.tsw.WriteLine("") // Finish the declaration line
|
|
183
|
+
return nil
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// --- Multi-variable declaration (existing logic seems okay, but less common for pointers) ---
|
|
187
|
+
c.tsw.WriteLiterally("let ")
|
|
188
|
+
c.tsw.WriteLiterally("[") // Use array destructuring for multi-assign
|
|
189
|
+
for i, name := range a.Names {
|
|
190
|
+
if i != 0 {
|
|
191
|
+
c.tsw.WriteLiterally(", ")
|
|
192
|
+
}
|
|
193
|
+
c.tsw.WriteLiterally(name.Name)
|
|
194
|
+
// TODO: Add type annotations for multi-var declarations if possible/needed
|
|
195
|
+
}
|
|
196
|
+
c.tsw.WriteLiterally("]")
|
|
197
|
+
if len(a.Values) > 0 {
|
|
198
|
+
// TODO: handle other kinds of assignment += -= etc.
|
|
199
|
+
c.tsw.WriteLiterally(" = ")
|
|
200
|
+
if len(a.Values) == 1 && len(a.Names) > 1 {
|
|
201
|
+
// Assign from a single multi-return value
|
|
202
|
+
if err := c.WriteValueExpr(a.Values[0]); err != nil {
|
|
203
|
+
return err
|
|
204
|
+
}
|
|
205
|
+
} else {
|
|
206
|
+
// Assign from multiple values
|
|
207
|
+
c.tsw.WriteLiterally("[")
|
|
208
|
+
for i, val := range a.Values {
|
|
209
|
+
if i != 0 {
|
|
210
|
+
c.tsw.WriteLiterally(", ")
|
|
211
|
+
}
|
|
212
|
+
if err := c.WriteValueExpr(val); err != nil { // Initializers are values
|
|
213
|
+
return err
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
c.tsw.WriteLiterally("]")
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
// No initializer, assign default values (complex for multi-assign)
|
|
220
|
+
// For simplicity, assign default array based on context (needs improvement)
|
|
221
|
+
c.tsw.WriteLiterally(" = []") // Placeholder
|
|
222
|
+
// TODO: Assign correct zero values based on types
|
|
223
|
+
}
|
|
224
|
+
c.tsw.WriteLine("") // Use WriteLine instead of WriteLine(";")
|
|
225
|
+
return nil
|
|
226
|
+
}
|
package/compiler/spec.go
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"go/ast"
|
|
6
|
+
"go/types"
|
|
7
|
+
"strings"
|
|
8
|
+
|
|
9
|
+
// types provides type information for Go types.
|
|
10
|
+
"github.com/pkg/errors"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
// WriteSpec is a dispatcher function that translates a Go specification node
|
|
14
|
+
// (`ast.Spec`) into its TypeScript equivalent. It handles different types of
|
|
15
|
+
// specifications found within `GenDecl` (general declarations):
|
|
16
|
+
// - `ast.ImportSpec` (import declarations): Delegates to `WriteImportSpec`.
|
|
17
|
+
// - `ast.ValueSpec` (variable or constant declarations): Delegates to `WriteValueSpec`.
|
|
18
|
+
// - `ast.TypeSpec` (type definitions like structs, interfaces): Delegates to `WriteTypeSpec`.
|
|
19
|
+
// If an unknown specification type is encountered, it returns an error.
|
|
20
|
+
func (c *GoToTSCompiler) WriteSpec(a ast.Spec) error {
|
|
21
|
+
switch d := a.(type) {
|
|
22
|
+
case *ast.ImportSpec:
|
|
23
|
+
c.WriteImportSpec(d)
|
|
24
|
+
case *ast.ValueSpec:
|
|
25
|
+
if err := c.WriteValueSpec(d); err != nil {
|
|
26
|
+
return err
|
|
27
|
+
}
|
|
28
|
+
case *ast.TypeSpec:
|
|
29
|
+
if err := c.WriteTypeSpec(d); err != nil {
|
|
30
|
+
return err
|
|
31
|
+
}
|
|
32
|
+
default:
|
|
33
|
+
return fmt.Errorf("unknown spec type: %T", a)
|
|
34
|
+
}
|
|
35
|
+
return nil
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
func (c *GoToTSCompiler) getEmbeddedFieldKeyName(fieldType types.Type) string {
|
|
39
|
+
trueType := fieldType
|
|
40
|
+
if ptr, isPtr := trueType.(*types.Pointer); isPtr {
|
|
41
|
+
trueType = ptr.Elem()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if named, isNamed := trueType.(*types.Named); isNamed {
|
|
45
|
+
return named.Obj().Name()
|
|
46
|
+
} else {
|
|
47
|
+
// Fallback for unnamed embedded types, though less common for structs
|
|
48
|
+
fieldKeyName := strings.Title(trueType.String()) // Simple heuristic
|
|
49
|
+
if dotIndex := strings.LastIndex(fieldKeyName, "."); dotIndex != -1 {
|
|
50
|
+
fieldKeyName = fieldKeyName[dotIndex+1:]
|
|
51
|
+
}
|
|
52
|
+
return fieldKeyName
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
func (c *GoToTSCompiler) writeGetterSetter(fieldName string, fieldType types.Type, doc, comment *ast.CommentGroup) {
|
|
57
|
+
fieldTypeStr := c.getTypeString(fieldType)
|
|
58
|
+
|
|
59
|
+
// Generate getter
|
|
60
|
+
if doc != nil {
|
|
61
|
+
c.WriteDoc(doc)
|
|
62
|
+
}
|
|
63
|
+
if comment != nil {
|
|
64
|
+
c.WriteDoc(comment)
|
|
65
|
+
}
|
|
66
|
+
c.tsw.WriteLinef("public get %s(): %s {", fieldName, fieldTypeStr)
|
|
67
|
+
c.tsw.Indent(1)
|
|
68
|
+
c.tsw.WriteLinef("return this._fields.%s.value", fieldName)
|
|
69
|
+
c.tsw.Indent(-1)
|
|
70
|
+
c.tsw.WriteLine("}")
|
|
71
|
+
|
|
72
|
+
// Generate setter (no comments)
|
|
73
|
+
c.tsw.WriteLinef("public set %s(value: %s) {", fieldName, fieldTypeStr)
|
|
74
|
+
c.tsw.Indent(1)
|
|
75
|
+
c.tsw.WriteLinef("this._fields.%s.value = value", fieldName)
|
|
76
|
+
c.tsw.Indent(-1)
|
|
77
|
+
c.tsw.WriteLine("}")
|
|
78
|
+
c.tsw.WriteLine("")
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
func (c *GoToTSCompiler) writeBoxedFieldInitializer(fieldName string, fieldType types.Type, isEmbedded bool) {
|
|
82
|
+
c.tsw.WriteLiterally(fieldName)
|
|
83
|
+
c.tsw.WriteLiterally(": $.box(")
|
|
84
|
+
|
|
85
|
+
if isEmbedded {
|
|
86
|
+
if _, isPtr := fieldType.(*types.Pointer); isPtr {
|
|
87
|
+
c.tsw.WriteLiterallyf("init?.%s ?? null", fieldName)
|
|
88
|
+
} else {
|
|
89
|
+
typeForNew := fieldName
|
|
90
|
+
c.tsw.WriteLiterallyf("new %s(init?.%s)", typeForNew, fieldName)
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
isStructValueType := false
|
|
94
|
+
var structTypeNameForClone string
|
|
95
|
+
if named, ok := fieldType.(*types.Named); ok {
|
|
96
|
+
if _, isStruct := named.Underlying().(*types.Struct); isStruct {
|
|
97
|
+
isStructValueType = true
|
|
98
|
+
structTypeNameForClone = named.Obj().Name()
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if isStructValueType {
|
|
103
|
+
c.tsw.WriteLiterallyf("init?.%s?.clone() ?? new %s()", fieldName, structTypeNameForClone)
|
|
104
|
+
} else {
|
|
105
|
+
c.tsw.WriteLiterallyf("init?.%s ?? ", fieldName)
|
|
106
|
+
c.WriteZeroValueForType(fieldType)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
c.tsw.WriteLiterally(")")
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
func (c *GoToTSCompiler) writeClonedFieldInitializer(fieldName string, fieldType types.Type, isEmbedded bool) {
|
|
114
|
+
c.tsw.WriteLiterally(fieldName)
|
|
115
|
+
c.tsw.WriteLiterally(": $.box(")
|
|
116
|
+
|
|
117
|
+
if isEmbedded {
|
|
118
|
+
isPointerToStruct := false
|
|
119
|
+
trueType := fieldType
|
|
120
|
+
if ptr, isPtr := trueType.(*types.Pointer); isPtr {
|
|
121
|
+
trueType = ptr.Elem()
|
|
122
|
+
isPointerToStruct = true
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if named, isNamed := trueType.(*types.Named); isNamed {
|
|
126
|
+
_, isUnderlyingStruct := named.Underlying().(*types.Struct)
|
|
127
|
+
if isUnderlyingStruct && !isPointerToStruct { // Is a value struct
|
|
128
|
+
c.tsw.WriteLiterallyf("this._fields.%s.value.clone()", fieldName)
|
|
129
|
+
} else { // Is a pointer to a struct, or not a struct
|
|
130
|
+
c.tsw.WriteLiterallyf("this._fields.%s.value", fieldName)
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
c.tsw.WriteLiterallyf("this._fields.%s.value", fieldName)
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
isValueTypeStruct := false
|
|
137
|
+
if named, ok := fieldType.(*types.Named); ok {
|
|
138
|
+
if _, isStruct := named.Underlying().(*types.Struct); isStruct {
|
|
139
|
+
isValueTypeStruct = true
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if isValueTypeStruct {
|
|
144
|
+
c.tsw.WriteLiterallyf("this._fields.%s.value?.clone() ?? null", fieldName)
|
|
145
|
+
} else {
|
|
146
|
+
c.tsw.WriteLiterallyf("this._fields.%s.value", fieldName)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
c.tsw.WriteLiterally(")")
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// WriteTypeSpec writes the type specification to the output.
|
|
154
|
+
func (c *GoToTSCompiler) WriteTypeSpec(a *ast.TypeSpec) error {
|
|
155
|
+
if a.Doc != nil {
|
|
156
|
+
c.WriteDoc(a.Doc)
|
|
157
|
+
}
|
|
158
|
+
if a.Comment != nil {
|
|
159
|
+
c.WriteDoc(a.Comment)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
switch t := a.Type.(type) {
|
|
163
|
+
case *ast.StructType:
|
|
164
|
+
return c.WriteStructTypeSpec(a, t)
|
|
165
|
+
case *ast.InterfaceType:
|
|
166
|
+
return c.WriteInterfaceTypeSpec(a, t)
|
|
167
|
+
default:
|
|
168
|
+
// type alias
|
|
169
|
+
c.tsw.WriteLiterally("type ")
|
|
170
|
+
if err := c.WriteValueExpr(a.Name); err != nil {
|
|
171
|
+
return err
|
|
172
|
+
}
|
|
173
|
+
c.tsw.WriteLiterally(" = ")
|
|
174
|
+
c.WriteTypeExpr(a.Type) // The aliased type
|
|
175
|
+
c.tsw.WriteLine(";")
|
|
176
|
+
}
|
|
177
|
+
return nil
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// WriteInterfaceTypeSpec writes the TypeScript type for a Go interface type.
|
|
181
|
+
func (c *GoToTSCompiler) WriteInterfaceTypeSpec(a *ast.TypeSpec, t *ast.InterfaceType) error {
|
|
182
|
+
c.tsw.WriteLiterally("type ")
|
|
183
|
+
if err := c.WriteValueExpr(a.Name); err != nil {
|
|
184
|
+
return err
|
|
185
|
+
}
|
|
186
|
+
c.tsw.WriteLiterally(" = ")
|
|
187
|
+
// Get the types.Interface from the ast.InterfaceType.
|
|
188
|
+
// For an interface definition like `type MyInterface interface { M() }`,
|
|
189
|
+
// 't' is the *ast.InterfaceType representing `interface { M() }`.
|
|
190
|
+
// TypesInfo.TypeOf(t) will give the *types.Interface.
|
|
191
|
+
goType := c.pkg.TypesInfo.TypeOf(t)
|
|
192
|
+
if goType == nil {
|
|
193
|
+
return errors.Errorf("could not get type for interface AST node for %s", a.Name.Name)
|
|
194
|
+
}
|
|
195
|
+
ifaceType, ok := goType.(*types.Interface)
|
|
196
|
+
if !ok {
|
|
197
|
+
return errors.Errorf("expected *types.Interface, got %T for %s when processing interface literal", goType, a.Name.Name)
|
|
198
|
+
}
|
|
199
|
+
c.WriteInterfaceType(ifaceType, t) // Pass the *ast.InterfaceType for comment fetching
|
|
200
|
+
c.tsw.WriteLine("")
|
|
201
|
+
|
|
202
|
+
// Add code to register the interface with the runtime system
|
|
203
|
+
interfaceName := a.Name.Name
|
|
204
|
+
c.tsw.WriteLine("")
|
|
205
|
+
c.tsw.WriteLinef("$.registerInterfaceType(")
|
|
206
|
+
c.tsw.WriteLinef(" '%s',", interfaceName)
|
|
207
|
+
c.tsw.WriteLinef(" null, // Zero value for interface is null")
|
|
208
|
+
|
|
209
|
+
// Collect methods for the interface type
|
|
210
|
+
var interfaceMethods []*types.Func
|
|
211
|
+
if ifaceType != nil { // ifaceType is *types.Interface
|
|
212
|
+
for i := range ifaceType.NumExplicitMethods() {
|
|
213
|
+
interfaceMethods = append(interfaceMethods, ifaceType.ExplicitMethod(i))
|
|
214
|
+
}
|
|
215
|
+
// TODO: Handle embedded interface methods if necessary for full signature collection.
|
|
216
|
+
// For now, explicit methods are covered.
|
|
217
|
+
}
|
|
218
|
+
c.tsw.WriteLiterally(" [")
|
|
219
|
+
c.writeMethodSignatures(interfaceMethods)
|
|
220
|
+
c.tsw.WriteLiterally("]")
|
|
221
|
+
c.tsw.WriteLine("")
|
|
222
|
+
|
|
223
|
+
c.tsw.WriteLinef(");")
|
|
224
|
+
|
|
225
|
+
return nil
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// WriteImportSpec translates a Go import specification (`ast.ImportSpec`)
|
|
229
|
+
// into a TypeScript import statement.
|
|
230
|
+
//
|
|
231
|
+
// It extracts the Go import path (e.g., `"path/to/pkg"`) and determines the
|
|
232
|
+
// import alias/name for TypeScript. If the Go import has an explicit name
|
|
233
|
+
// (e.g., `alias "path/to/pkg"`), that alias is used. Otherwise, the package
|
|
234
|
+
// name is derived from the Go path.
|
|
235
|
+
//
|
|
236
|
+
// The Go path is then translated to a TypeScript module path using
|
|
237
|
+
// `translateGoPathToTypescriptPath`.
|
|
238
|
+
//
|
|
239
|
+
// Finally, it writes a TypeScript import statement like `import * as alias from "typescript/path/to/pkg";`
|
|
240
|
+
// and records the import details in `c.analysis.Imports` for later use (e.g.,
|
|
241
|
+
// resolving qualified identifiers).
|
|
242
|
+
func (c *GoToTSCompiler) WriteImportSpec(a *ast.ImportSpec) {
|
|
243
|
+
if a.Doc != nil {
|
|
244
|
+
c.WriteDoc(a.Doc)
|
|
245
|
+
}
|
|
246
|
+
if a.Comment != nil {
|
|
247
|
+
c.WriteDoc(a.Comment)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
goPath := a.Path.Value[1 : len(a.Path.Value)-1]
|
|
251
|
+
impName := packageNameFromGoPath(goPath)
|
|
252
|
+
if a.Name != nil && a.Name.Name != "" {
|
|
253
|
+
impName = a.Name.Name
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// All Go package imports are mapped to the @goscript/ scope.
|
|
257
|
+
// The TypeScript compiler will resolve these using tsconfig paths to either
|
|
258
|
+
// handwritten versions (in .goscript-assets) or transpiled versions (in goscript).
|
|
259
|
+
var tsImportPath string
|
|
260
|
+
if goPath == "github.com/aperturerobotics/goscript/builtin" {
|
|
261
|
+
tsImportPath = "@goscript/builtin/builtin.js"
|
|
262
|
+
} else {
|
|
263
|
+
tsImportPath = "@goscript/" + goPath
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
c.analysis.Imports[impName] = &fileImport{
|
|
267
|
+
importPath: tsImportPath,
|
|
268
|
+
importVars: make(map[string]struct{}),
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
c.tsw.WriteImport(impName, tsImportPath+"/index.js")
|
|
272
|
+
}
|