goscript 0.0.21 → 0.0.22
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/builtin/builtin.ts +298 -33
- package/compiler/assignment.go +407 -0
- package/compiler/compiler.go +160 -5883
- package/compiler/compiler_test.go +4 -0
- package/compiler/composite-lit.go +537 -0
- package/compiler/config.go +3 -0
- package/compiler/decl.go +223 -0
- package/compiler/expr-call.go +423 -0
- package/compiler/expr-selector.go +105 -0
- package/compiler/expr-star.go +90 -0
- package/compiler/expr-type.go +182 -0
- package/compiler/expr-value.go +119 -0
- package/compiler/expr.go +356 -0
- package/compiler/field.go +169 -0
- package/compiler/lit.go +99 -0
- package/compiler/primitive.go +142 -0
- package/compiler/{write-type-spec.go → spec-struct.go} +79 -203
- package/compiler/spec-value.go +194 -0
- package/compiler/spec.go +263 -0
- package/compiler/stmt-assign.go +411 -0
- package/compiler/stmt-for.go +178 -0
- package/compiler/stmt-range.go +237 -0
- package/compiler/stmt.go +907 -0
- package/compiler/type-assert.go +463 -0
- package/compiler/type-info.go +141 -0
- package/compiler/type.go +556 -0
- package/dist/builtin/builtin.d.ts +24 -6
- package/dist/builtin/builtin.js +215 -19
- package/dist/builtin/builtin.js.map +1 -1
- package/go.mod +2 -1
- package/go.sum +4 -2
- package/package.json +2 -2
- /package/compiler/{writer.go → code-writer.go} +0 -0
|
@@ -5,6 +5,7 @@ import (
|
|
|
5
5
|
"os/exec"
|
|
6
6
|
"path/filepath"
|
|
7
7
|
"runtime"
|
|
8
|
+
"slices"
|
|
8
9
|
"sync"
|
|
9
10
|
"sync/atomic"
|
|
10
11
|
"testing"
|
|
@@ -43,6 +44,9 @@ func TestCompliance(t *testing.T) {
|
|
|
43
44
|
testPaths = append(testPaths, testPath)
|
|
44
45
|
}
|
|
45
46
|
|
|
47
|
+
// sort testPaths
|
|
48
|
+
slices.Sort(testPaths)
|
|
49
|
+
|
|
46
50
|
// limit concurrency
|
|
47
51
|
simulLimit := make(chan struct{}, runtime.GOMAXPROCS(-1)*2)
|
|
48
52
|
for range cap(simulLimit) {
|
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"go/ast"
|
|
6
|
+
"go/token"
|
|
7
|
+
"go/types"
|
|
8
|
+
"slices"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// WriteCompositeLit translates a Go composite literal (ast.CompositeLit) into its
|
|
12
|
+
// TypeScript equivalent.
|
|
13
|
+
//
|
|
14
|
+
// It handles several types of composite literals:
|
|
15
|
+
// - Map literals (e.g., `map[K]V{k1: v1}`): Translated to `new Map([[k1_ts, v1_ts]])`.
|
|
16
|
+
// Values are processed by `writeBoxedValue`.
|
|
17
|
+
// - Array/Slice literals (e.g., `[]T{e1, e2}`, `[N]T{idx: val}`):
|
|
18
|
+
// Translated using the `$.arrayToSlice<T_ts>([...])` runtime helper.
|
|
19
|
+
// It handles both keyed and unkeyed elements, infers length if necessary,
|
|
20
|
+
// and uses zero values for uninitialized array elements.
|
|
21
|
+
// Multi-dimensional arrays/slices pass a depth parameter to `$.arrayToSlice`.
|
|
22
|
+
// Element values are processed by `writeBoxedValue`.
|
|
23
|
+
// - Struct literals:
|
|
24
|
+
// - Named structs (e.g., `MyStruct{F: v}` or `&MyStruct{F: v}`): Translated to
|
|
25
|
+
// `new MyStruct_ts({ F: v_ts, ... })`. The constructor typically uses an `_init` method.
|
|
26
|
+
// - Anonymous structs (e.g., `struct{F int}{F: v}`): Translated to TypeScript
|
|
27
|
+
// object literals `{ F: v_ts, ... }`.
|
|
28
|
+
// It processes keyed elements (`FieldName: Value`) and unkeyed elements (for anonymous
|
|
29
|
+
// structs or arrays). Field values are processed by `writeBoxedValue`.
|
|
30
|
+
// Embedded struct fields are initialized, and explicit initializers for embedded
|
|
31
|
+
// structs (e.g. `Outer{InnerField: InnerType{...}}`) are handled.
|
|
32
|
+
// The function uses `c.analysis` to determine correct value access (e.g., `.value` for boxed fields).
|
|
33
|
+
func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
34
|
+
// Get the type of the composite literal
|
|
35
|
+
litType := c.pkg.TypesInfo.TypeOf(exp)
|
|
36
|
+
if exp.Type != nil {
|
|
37
|
+
// Handle map literals: map[K]V{k1: v1, k2: v2}
|
|
38
|
+
if _, isMapType := exp.Type.(*ast.MapType); isMapType {
|
|
39
|
+
c.tsw.WriteLiterally("new Map([")
|
|
40
|
+
|
|
41
|
+
// Add each key-value pair as an entry
|
|
42
|
+
for i, elm := range exp.Elts {
|
|
43
|
+
if i > 0 {
|
|
44
|
+
c.tsw.WriteLiterally(", ")
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if kv, ok := elm.(*ast.KeyValueExpr); ok {
|
|
48
|
+
c.tsw.WriteLiterally("[")
|
|
49
|
+
if err := c.WriteBoxedValue(kv.Key); err != nil {
|
|
50
|
+
return fmt.Errorf("failed to write map literal key: %w", err)
|
|
51
|
+
}
|
|
52
|
+
c.tsw.WriteLiterally(", ")
|
|
53
|
+
if err := c.WriteBoxedValue(kv.Value); err != nil {
|
|
54
|
+
return fmt.Errorf("failed to write map literal value: %w", err)
|
|
55
|
+
}
|
|
56
|
+
c.tsw.WriteLiterally("]")
|
|
57
|
+
} else {
|
|
58
|
+
return fmt.Errorf("map literal elements must be key-value pairs")
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
c.tsw.WriteLiterally("])")
|
|
63
|
+
return nil
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Handle array literals
|
|
67
|
+
if arrType, isArrayType := exp.Type.(*ast.ArrayType); isArrayType {
|
|
68
|
+
// Check if this is a slice of slices (multi-dimensional array)
|
|
69
|
+
isMultiDimensional := false
|
|
70
|
+
if _, ok := arrType.Elt.(*ast.ArrayType); ok {
|
|
71
|
+
// It's a slice of slices (multi-dimensional array)
|
|
72
|
+
isMultiDimensional = true
|
|
73
|
+
// We'll handle this with depth parameter to arrayToSlice
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
c.tsw.WriteLiterally("$.arrayToSlice")
|
|
77
|
+
|
|
78
|
+
// write the type annotation
|
|
79
|
+
c.tsw.WriteLiterally("<")
|
|
80
|
+
// Write the element type using the existing function
|
|
81
|
+
c.WriteTypeExpr(arrType.Elt)
|
|
82
|
+
c.tsw.WriteLiterally(">")
|
|
83
|
+
|
|
84
|
+
c.tsw.WriteLiterally("([")
|
|
85
|
+
|
|
86
|
+
// Use type info to get array length and element type
|
|
87
|
+
var arrayLen int
|
|
88
|
+
var elemType ast.Expr
|
|
89
|
+
var goElemType interface{}
|
|
90
|
+
if typ := c.pkg.TypesInfo.TypeOf(exp.Type); typ != nil {
|
|
91
|
+
if at, ok := typ.Underlying().(*types.Array); ok {
|
|
92
|
+
arrayLen = int(at.Len())
|
|
93
|
+
goElemType = at.Elem()
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if arrType.Len != nil {
|
|
97
|
+
// Try to evaluate the length from the AST if not available from type info
|
|
98
|
+
if bl, ok := arrType.Len.(*ast.BasicLit); ok && bl.Kind == token.INT {
|
|
99
|
+
if _, err := fmt.Sscan(bl.Value, &arrayLen); err != nil {
|
|
100
|
+
return fmt.Errorf("failed to parse array length from basic literal: %w", err)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
elemType = arrType.Elt
|
|
105
|
+
|
|
106
|
+
// Map of index -> value
|
|
107
|
+
elements := make(map[int]ast.Expr)
|
|
108
|
+
orderedCount := 0
|
|
109
|
+
maxIndex := -1
|
|
110
|
+
hasKeyedElements := false
|
|
111
|
+
|
|
112
|
+
for _, elm := range exp.Elts {
|
|
113
|
+
if kv, ok := elm.(*ast.KeyValueExpr); ok {
|
|
114
|
+
if lit, ok := kv.Key.(*ast.BasicLit); ok && lit.Kind == token.INT {
|
|
115
|
+
var index int
|
|
116
|
+
if _, err := fmt.Sscan(lit.Value, &index); err != nil {
|
|
117
|
+
return fmt.Errorf("failed to parse array index from basic literal: %w", err)
|
|
118
|
+
}
|
|
119
|
+
elements[index] = kv.Value
|
|
120
|
+
if index > maxIndex {
|
|
121
|
+
maxIndex = index
|
|
122
|
+
}
|
|
123
|
+
hasKeyedElements = true
|
|
124
|
+
} else {
|
|
125
|
+
c.tsw.WriteCommentInline("unhandled keyed array literal key type")
|
|
126
|
+
if err := c.WriteBoxedValue(elm); err != nil {
|
|
127
|
+
return fmt.Errorf("failed to write keyed array literal element with unhandled key type: %w", err)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
elements[orderedCount] = elm
|
|
132
|
+
if orderedCount > maxIndex {
|
|
133
|
+
maxIndex = orderedCount
|
|
134
|
+
}
|
|
135
|
+
orderedCount++
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Determine array length
|
|
140
|
+
if arrayLen == 0 {
|
|
141
|
+
// If length is not set, infer from max index or number of elements
|
|
142
|
+
if hasKeyedElements {
|
|
143
|
+
arrayLen = maxIndex + 1
|
|
144
|
+
} else {
|
|
145
|
+
arrayLen = len(exp.Elts)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
for i := 0; i < arrayLen; i++ {
|
|
150
|
+
if i > 0 {
|
|
151
|
+
c.tsw.WriteLiterally(", ")
|
|
152
|
+
}
|
|
153
|
+
if elm, ok := elements[i]; ok && elm != nil {
|
|
154
|
+
if err := c.WriteBoxedValue(elm); err != nil {
|
|
155
|
+
return fmt.Errorf("failed to write array literal element: %w", err)
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
// Write zero value for element type
|
|
159
|
+
if goElemType != nil {
|
|
160
|
+
c.WriteZeroValueForType(goElemType)
|
|
161
|
+
} else {
|
|
162
|
+
c.WriteZeroValueForType(elemType)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
c.tsw.WriteLiterally("]")
|
|
167
|
+
|
|
168
|
+
// If it's a multi-dimensional array/slice, use depth=2 to convert nested arrays
|
|
169
|
+
if isMultiDimensional {
|
|
170
|
+
c.tsw.WriteLiterally(", 2") // Depth of 2 for one level of nesting
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
c.tsw.WriteLiterally(")")
|
|
174
|
+
return nil
|
|
175
|
+
} else {
|
|
176
|
+
// Check if this is a struct type
|
|
177
|
+
var structType *types.Struct
|
|
178
|
+
isStructLiteral := false
|
|
179
|
+
isAnonymousStruct := false
|
|
180
|
+
|
|
181
|
+
if namedType, ok := litType.(*types.Named); ok {
|
|
182
|
+
if underlyingStruct, ok := namedType.Underlying().(*types.Struct); ok {
|
|
183
|
+
structType = underlyingStruct
|
|
184
|
+
isStructLiteral = true
|
|
185
|
+
// Named struct, use constructor
|
|
186
|
+
c.tsw.WriteLiterally("new ")
|
|
187
|
+
c.WriteTypeExpr(exp.Type)
|
|
188
|
+
}
|
|
189
|
+
} else if ptrType, ok := litType.(*types.Pointer); ok {
|
|
190
|
+
if namedElem, ok := ptrType.Elem().(*types.Named); ok {
|
|
191
|
+
if underlyingStruct, ok := namedElem.Underlying().(*types.Struct); ok {
|
|
192
|
+
structType = underlyingStruct
|
|
193
|
+
isStructLiteral = true // Treat pointer-to-struct literal similarly
|
|
194
|
+
// Named struct pointer, use constructor
|
|
195
|
+
c.tsw.WriteLiterally("new ")
|
|
196
|
+
c.WriteTypeExpr(exp.Type)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
} else if underlyingStruct, ok := litType.Underlying().(*types.Struct); ok {
|
|
200
|
+
// Anonymous struct literal
|
|
201
|
+
structType = underlyingStruct
|
|
202
|
+
isStructLiteral = true
|
|
203
|
+
isAnonymousStruct = true
|
|
204
|
+
// For anonymous structs, don't use constructor, just create object literal
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if isStructLiteral && structType != nil {
|
|
208
|
+
// --- Struct Literal Handling (Nested) ---
|
|
209
|
+
directFields := make(map[string]ast.Expr)
|
|
210
|
+
embeddedFields := make(map[string]map[string]ast.Expr) // map[EmbeddedPropName]map[FieldName]ValueExpr
|
|
211
|
+
explicitEmbedded := make(map[string]ast.Expr) // Tracks explicitly initialized embedded structs
|
|
212
|
+
|
|
213
|
+
// Pre-populate embeddedFields map keys using the correct property name
|
|
214
|
+
for i := 0; i < structType.NumFields(); i++ {
|
|
215
|
+
field := structType.Field(i)
|
|
216
|
+
if field.Anonymous() {
|
|
217
|
+
fieldType := field.Type()
|
|
218
|
+
if ptr, ok := fieldType.(*types.Pointer); ok {
|
|
219
|
+
fieldType = ptr.Elem()
|
|
220
|
+
}
|
|
221
|
+
if named, ok := fieldType.(*types.Named); ok {
|
|
222
|
+
// Use the type name as the property name in TS
|
|
223
|
+
embeddedPropName := named.Obj().Name()
|
|
224
|
+
embeddedFields[embeddedPropName] = make(map[string]ast.Expr)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Group literal elements by direct vs embedded fields
|
|
230
|
+
for _, elt := range exp.Elts {
|
|
231
|
+
kv, ok := elt.(*ast.KeyValueExpr)
|
|
232
|
+
if !ok {
|
|
233
|
+
continue
|
|
234
|
+
} // Skip non-key-value
|
|
235
|
+
keyIdent, ok := kv.Key.(*ast.Ident)
|
|
236
|
+
if !ok {
|
|
237
|
+
continue
|
|
238
|
+
} // Skip non-ident keys
|
|
239
|
+
keyName := keyIdent.Name
|
|
240
|
+
|
|
241
|
+
// Check if this is an explicit embedded struct initialization
|
|
242
|
+
// e.g., Person: Person{...} or Person: personVar
|
|
243
|
+
if _, isEmbedded := embeddedFields[keyName]; isEmbedded {
|
|
244
|
+
// This is an explicit initialization of an embedded struct
|
|
245
|
+
explicitEmbedded[keyName] = kv.Value
|
|
246
|
+
continue
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
isDirectField := false
|
|
250
|
+
for i := range structType.NumFields() {
|
|
251
|
+
field := structType.Field(i)
|
|
252
|
+
if field.Name() == keyName {
|
|
253
|
+
isDirectField = true
|
|
254
|
+
directFields[keyName] = kv.Value
|
|
255
|
+
break
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// For anonymous structs, all fields are direct fields
|
|
260
|
+
if isAnonymousStruct {
|
|
261
|
+
directFields[keyName] = kv.Value
|
|
262
|
+
isDirectField = true
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// If not a direct field, return an error
|
|
266
|
+
if !isDirectField {
|
|
267
|
+
// This field was not found as a direct field in the struct
|
|
268
|
+
return fmt.Errorf("field %s not found in type %s for composite literal",
|
|
269
|
+
keyName, litType.String())
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Handle the case where an anonymous struct has values without keys
|
|
274
|
+
// Handle the case where an anonymous struct has values without keys.
|
|
275
|
+
// This block processes non-key-value elements and associates them with struct fields.
|
|
276
|
+
if isAnonymousStruct && len(exp.Elts) > 0 && len(directFields) == 0 && structType != nil {
|
|
277
|
+
// Check if any elements in the composite literal are not key-value pairs.
|
|
278
|
+
hasNonKeyValueElts := false
|
|
279
|
+
for _, elt := range exp.Elts {
|
|
280
|
+
// If an element is not a key-value pair, set the flag to true.
|
|
281
|
+
if _, isKV := elt.(*ast.KeyValueExpr); !isKV {
|
|
282
|
+
hasNonKeyValueElts = true
|
|
283
|
+
break
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if hasNonKeyValueElts {
|
|
288
|
+
// Get the fields from the struct type
|
|
289
|
+
for i := 0; i < structType.NumFields(); i++ {
|
|
290
|
+
field := structType.Field(i)
|
|
291
|
+
// If we have a value for this field position
|
|
292
|
+
if i < len(exp.Elts) {
|
|
293
|
+
// Check if it's not a key-value pair
|
|
294
|
+
if _, isKV := exp.Elts[i].(*ast.KeyValueExpr); !isKV {
|
|
295
|
+
directFields[field.Name()] = exp.Elts[i]
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Write the object literal
|
|
303
|
+
if isAnonymousStruct {
|
|
304
|
+
// For anonymous structs, just write a simple object literal
|
|
305
|
+
c.tsw.WriteLiterally("{")
|
|
306
|
+
} else {
|
|
307
|
+
// For named structs, write the constructor argument
|
|
308
|
+
c.tsw.WriteLiterally("({")
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
firstFieldWritten := false
|
|
312
|
+
|
|
313
|
+
// Write direct fields that aren't embedded struct names
|
|
314
|
+
directKeys := make([]string, 0, len(directFields))
|
|
315
|
+
for k := range directFields {
|
|
316
|
+
// Skip embedded struct names - we'll handle those separately
|
|
317
|
+
if _, isEmbedded := embeddedFields[k]; !isEmbedded {
|
|
318
|
+
directKeys = append(directKeys, k)
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
slices.Sort(directKeys)
|
|
322
|
+
for _, keyName := range directKeys {
|
|
323
|
+
if firstFieldWritten {
|
|
324
|
+
c.tsw.WriteLiterally(", ")
|
|
325
|
+
}
|
|
326
|
+
c.tsw.WriteLiterally(keyName)
|
|
327
|
+
c.tsw.WriteLiterally(": ")
|
|
328
|
+
if err := c.WriteBoxedValue(directFields[keyName]); err != nil {
|
|
329
|
+
return err
|
|
330
|
+
}
|
|
331
|
+
firstFieldWritten = true
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Write explicitly initialized embedded structs
|
|
335
|
+
explicitKeys := make([]string, 0, len(explicitEmbedded))
|
|
336
|
+
for k := range explicitEmbedded {
|
|
337
|
+
explicitKeys = append(explicitKeys, k)
|
|
338
|
+
}
|
|
339
|
+
slices.Sort(explicitKeys)
|
|
340
|
+
for _, embeddedName := range explicitKeys {
|
|
341
|
+
if firstFieldWritten {
|
|
342
|
+
c.tsw.WriteLiterally(", ")
|
|
343
|
+
}
|
|
344
|
+
c.tsw.WriteLiterally(embeddedName)
|
|
345
|
+
c.tsw.WriteLiterally(": ")
|
|
346
|
+
|
|
347
|
+
// Check if the embedded value is a composite literal for a struct
|
|
348
|
+
// If so, extract the fields and write them directly
|
|
349
|
+
if compLit, ok := explicitEmbedded[embeddedName].(*ast.CompositeLit); ok {
|
|
350
|
+
// Write initialization fields directly without the 'new Constructor'
|
|
351
|
+
c.tsw.WriteLiterally("{")
|
|
352
|
+
for i, elem := range compLit.Elts {
|
|
353
|
+
if i > 0 {
|
|
354
|
+
c.tsw.WriteLiterally(", ")
|
|
355
|
+
}
|
|
356
|
+
if err := c.WriteBoxedValue(elem); err != nil {
|
|
357
|
+
return err
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
c.tsw.WriteLiterally("}")
|
|
361
|
+
} else {
|
|
362
|
+
// Not a composite literal, write it normally
|
|
363
|
+
if err := c.WriteBoxedValue(explicitEmbedded[embeddedName]); err != nil {
|
|
364
|
+
return err
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
firstFieldWritten = true
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Write embedded fields for structs that weren't explicitly initialized
|
|
371
|
+
embeddedKeys := make([]string, 0, len(embeddedFields))
|
|
372
|
+
for k := range embeddedFields {
|
|
373
|
+
// Skip embedded structs that were explicitly initialized
|
|
374
|
+
if _, wasExplicit := explicitEmbedded[k]; !wasExplicit {
|
|
375
|
+
embeddedKeys = append(embeddedKeys, k)
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
slices.Sort(embeddedKeys)
|
|
379
|
+
for _, embeddedPropName := range embeddedKeys {
|
|
380
|
+
fieldsMap := embeddedFields[embeddedPropName]
|
|
381
|
+
if len(fieldsMap) == 0 {
|
|
382
|
+
continue
|
|
383
|
+
} // Skip empty embedded initializers
|
|
384
|
+
|
|
385
|
+
if firstFieldWritten {
|
|
386
|
+
c.tsw.WriteLiterally(", ")
|
|
387
|
+
}
|
|
388
|
+
c.tsw.WriteLiterally(embeddedPropName) // Use the Type name as the property key
|
|
389
|
+
c.tsw.WriteLiterally(": {")
|
|
390
|
+
|
|
391
|
+
innerKeys := make([]string, 0, len(fieldsMap))
|
|
392
|
+
for k := range fieldsMap {
|
|
393
|
+
innerKeys = append(innerKeys, k)
|
|
394
|
+
}
|
|
395
|
+
slices.Sort(innerKeys)
|
|
396
|
+
for i, keyName := range innerKeys {
|
|
397
|
+
if i > 0 {
|
|
398
|
+
c.tsw.WriteLiterally(", ")
|
|
399
|
+
}
|
|
400
|
+
c.tsw.WriteLiterally(keyName) // Field name within the embedded struct
|
|
401
|
+
c.tsw.WriteLiterally(": ")
|
|
402
|
+
if err := c.WriteBoxedValue(fieldsMap[keyName]); err != nil {
|
|
403
|
+
return err
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
c.tsw.WriteLiterally("}")
|
|
407
|
+
firstFieldWritten = true
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Close the object literal
|
|
411
|
+
if isAnonymousStruct {
|
|
412
|
+
c.tsw.WriteLiterally("}")
|
|
413
|
+
} else {
|
|
414
|
+
c.tsw.WriteLiterally("})")
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
} else {
|
|
418
|
+
// Non-struct type or anonymous struct, handle normally (or potentially error for anonymous struct literals?)
|
|
419
|
+
c.tsw.WriteLiterally("({") // Assuming object literal for constructor
|
|
420
|
+
for i, elm := range exp.Elts {
|
|
421
|
+
if i != 0 {
|
|
422
|
+
c.tsw.WriteLiterally(", ")
|
|
423
|
+
}
|
|
424
|
+
if err := c.WriteBoxedValue(elm); err != nil {
|
|
425
|
+
return fmt.Errorf("failed to write literal field: %w", err)
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
c.tsw.WriteLiterally("})")
|
|
429
|
+
}
|
|
430
|
+
return nil
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Untyped composite literal. Let's use type information to determine what it is.
|
|
435
|
+
// First try to get the type information for the expression
|
|
436
|
+
isObject := false
|
|
437
|
+
if tv, ok := c.pkg.TypesInfo.Types[exp]; ok && tv.Type != nil {
|
|
438
|
+
underlying := tv.Type.Underlying()
|
|
439
|
+
switch underlying.(type) {
|
|
440
|
+
case *types.Map, *types.Struct:
|
|
441
|
+
isObject = true
|
|
442
|
+
case *types.Array, *types.Slice:
|
|
443
|
+
isObject = false
|
|
444
|
+
default:
|
|
445
|
+
return fmt.Errorf("unhandled composite literal type: %T", underlying)
|
|
446
|
+
}
|
|
447
|
+
} else {
|
|
448
|
+
return fmt.Errorf("could not determine composite literal type from type information")
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if isObject {
|
|
452
|
+
c.tsw.WriteLiterally("{ ")
|
|
453
|
+
} else {
|
|
454
|
+
c.tsw.WriteLiterally("[ ")
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
for i, elm := range exp.Elts {
|
|
458
|
+
if i != 0 {
|
|
459
|
+
c.tsw.WriteLiterally(", ")
|
|
460
|
+
}
|
|
461
|
+
if err := c.WriteBoxedValue(elm); err != nil {
|
|
462
|
+
return fmt.Errorf("failed to write untyped composite literal element: %w", err)
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
if isObject {
|
|
466
|
+
c.tsw.WriteLiterally(" }")
|
|
467
|
+
} else {
|
|
468
|
+
c.tsw.WriteLiterally(" ]")
|
|
469
|
+
}
|
|
470
|
+
return nil
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// WriteBoxedValue translates a Go expression (`ast.Expr`) into its TypeScript equivalent,
|
|
474
|
+
// specifically for use as a value within a composite literal (e.g., struct fields,
|
|
475
|
+
// map keys/values, or array/slice elements). Its primary goal is to ensure that the
|
|
476
|
+
// actual, unboxed value of the expression is used.
|
|
477
|
+
//
|
|
478
|
+
// How it works:
|
|
479
|
+
// - Identifiers (`*ast.Ident`): Delegates to `c.WriteIdent(ident, true)`, forcing
|
|
480
|
+
// the `accessValue` flag to `true`. This ensures that if `ident` refers to a
|
|
481
|
+
// GoScript boxed variable, the generated TypeScript accesses its underlying `.value`
|
|
482
|
+
// (e.g., `myVar.value`).
|
|
483
|
+
// - Selector Expressions (`*ast.SelectorExpr`, e.g., `obj.Field`): Delegates to
|
|
484
|
+
// `c.WriteSelectorExpr(e)`. This function handles the necessary logic for
|
|
485
|
+
// accessing fields or methods, including any required unboxing if the field
|
|
486
|
+
// itself or the object it's accessed on is boxed (e.g., `obj.value.field` or
|
|
487
|
+
// `obj.field.value`).
|
|
488
|
+
// - Star Expressions (`*ast.StarExpr`, e.g., `*ptr`): Delegates to `c.WriteStarExpr(e)`.
|
|
489
|
+
// This function handles pointer dereferencing, which in GoScript's boxing model
|
|
490
|
+
// often translates to accessing the `.value` field of the pointer (e.g., `ptr.value`).
|
|
491
|
+
// - Basic Literals (`*ast.BasicLit`, e.g., `123`, `"hello"`): Delegates to
|
|
492
|
+
// `c.WriteBasicLit(e)` for direct translation.
|
|
493
|
+
// - Other expression types: Falls back to `c.WriteValueExpr(expr)` for general
|
|
494
|
+
// expression handling. This is important for complex expressions like function
|
|
495
|
+
// calls or binary operations that might appear as values within a composite literal.
|
|
496
|
+
//
|
|
497
|
+
// Necessity and Distinction from `WriteValueExpr`:
|
|
498
|
+
// While `WriteValueExpr` is a general-purpose function for translating Go expressions
|
|
499
|
+
// and also unboxes identifiers (by calling `WriteIdent` with `accessValue: true`),
|
|
500
|
+
// `WriteBoxedValue` serves a specific and crucial role when called from `WriteCompositeLit`:
|
|
501
|
+
// 1. Clarity of Intent: It explicitly signals that for the constituents of a composite
|
|
502
|
+
// literal, the *unboxed value* is mandatory.
|
|
503
|
+
// 2. Contract for `WriteCompositeLit`: It ensures that `WriteCompositeLit` receives
|
|
504
|
+
// the correct values for initialization, insulating it from potential changes in
|
|
505
|
+
// the default behavior of `WriteValueExpr` regarding unboxing.
|
|
506
|
+
// 3. Prevents Recursion: `WriteValueExpr` handles `*ast.CompositeLit` nodes by
|
|
507
|
+
// calling `WriteCompositeLit`. If `WriteCompositeLit` were to directly call
|
|
508
|
+
// `WriteValueExpr` for its elements, it could lead to unintended recursion or
|
|
509
|
+
// behavior if an element itself was another composite literal. `WriteBoxedValue`
|
|
510
|
+
// acts as a specific intermediary for the *elements*.
|
|
511
|
+
//
|
|
512
|
+
// In summary, `WriteBoxedValue` is a specialized dispatcher used by `WriteCompositeLit`
|
|
513
|
+
// to guarantee that all parts of a Go composite literal are initialized with their
|
|
514
|
+
// proper, unboxed TypeScript values.
|
|
515
|
+
func (c *GoToTSCompiler) WriteBoxedValue(expr ast.Expr) error {
|
|
516
|
+
if expr == nil {
|
|
517
|
+
return fmt.Errorf("nil expression passed to writeBoxedValue")
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Handle different expression types
|
|
521
|
+
switch e := expr.(type) {
|
|
522
|
+
case *ast.Ident:
|
|
523
|
+
c.WriteIdent(e, true)
|
|
524
|
+
return nil
|
|
525
|
+
case *ast.SelectorExpr:
|
|
526
|
+
return c.WriteSelectorExpr(e)
|
|
527
|
+
case *ast.StarExpr:
|
|
528
|
+
// For star expressions, delegate to WriteStarExpr which handles dereferencing
|
|
529
|
+
return c.WriteStarExpr(e)
|
|
530
|
+
case *ast.BasicLit:
|
|
531
|
+
c.WriteBasicLit(e)
|
|
532
|
+
return nil
|
|
533
|
+
default:
|
|
534
|
+
// For other expression types, use WriteValueExpr
|
|
535
|
+
return c.WriteValueExpr(expr)
|
|
536
|
+
}
|
|
537
|
+
}
|
package/compiler/config.go
CHANGED
|
@@ -17,6 +17,9 @@ type Config struct {
|
|
|
17
17
|
OutputPathRoot string
|
|
18
18
|
// BuildFlags are the Go build flags (tags) to use during analysis.
|
|
19
19
|
BuildFlags []string
|
|
20
|
+
// AllDependencies controls whether to compile all dependencies of the requested packages.
|
|
21
|
+
// If true, all dependencies will be compiled; if false, only the requested packages are compiled.
|
|
22
|
+
AllDependencies bool
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
// Validate checks the config.
|