goscript 0.0.20 → 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 +390 -119
- package/compiler/assignment.go +407 -0
- package/compiler/compiler.go +203 -5879
- 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/output.go +1 -1
- 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
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"go/ast"
|
|
6
|
+
"go/types"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// WriteSelectorExpr translates a Go selector expression (`ast.SelectorExpr`)
|
|
10
|
+
// used as a value (e.g., `obj.Field`, `pkg.Variable`, `structVar.Method()`)
|
|
11
|
+
// into its TypeScript equivalent.
|
|
12
|
+
// It distinguishes between package selectors (e.g., `time.Now`) and field/method
|
|
13
|
+
// access on an object or struct.
|
|
14
|
+
// - For package selectors, it writes `PackageName.IdentifierName`. The `IdentifierName`
|
|
15
|
+
// is written using `WriteIdent` which handles potential `.value` access if the
|
|
16
|
+
// package-level variable is boxed.
|
|
17
|
+
// - For field or method access on an object (`exp.X`), it first writes the base
|
|
18
|
+
// expression (`exp.X`) using `WriteValueExpr` (which handles its own boxing).
|
|
19
|
+
// Then, it writes a dot (`.`) followed by the selected identifier (`exp.Sel`)
|
|
20
|
+
// using `WriteIdent`, which appends `.value` if the field itself is boxed
|
|
21
|
+
// (e.g., accessing a field of primitive type through a pointer to a struct
|
|
22
|
+
// where the field's address might have been taken).
|
|
23
|
+
//
|
|
24
|
+
// This function aims to correctly navigate Go's automatic dereferencing and
|
|
25
|
+
// TypeScript's explicit boxing model.
|
|
26
|
+
func (c *GoToTSCompiler) WriteSelectorExpr(exp *ast.SelectorExpr) error {
|
|
27
|
+
// Check if this is a package selector (e.g., time.Now)
|
|
28
|
+
if pkgIdent, isPkgIdent := exp.X.(*ast.Ident); isPkgIdent {
|
|
29
|
+
if obj := c.pkg.TypesInfo.ObjectOf(pkgIdent); obj != nil {
|
|
30
|
+
if _, isPkg := obj.(*types.PkgName); isPkg {
|
|
31
|
+
// Package selectors should never use .value on the package name
|
|
32
|
+
c.tsw.WriteLiterally(pkgIdent.Name)
|
|
33
|
+
c.tsw.WriteLiterally(".")
|
|
34
|
+
// Write the selected identifier, allowing .value if it's a boxed package variable
|
|
35
|
+
c.WriteIdent(exp.Sel, true)
|
|
36
|
+
return nil
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// --- Special case for dereferenced pointer to struct with field access: (*p).field ---
|
|
42
|
+
var baseExpr ast.Expr = exp.X
|
|
43
|
+
// Look inside parentheses if present
|
|
44
|
+
if parenExpr, isParen := exp.X.(*ast.ParenExpr); isParen {
|
|
45
|
+
baseExpr = parenExpr.X
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if starExpr, isStarExpr := baseExpr.(*ast.StarExpr); isStarExpr {
|
|
49
|
+
// Get the type of the pointer being dereferenced (e.g., type of 'p' in *p)
|
|
50
|
+
ptrType := c.pkg.TypesInfo.TypeOf(starExpr.X)
|
|
51
|
+
if ptrType != nil {
|
|
52
|
+
if ptrTypeUnwrapped, ok := ptrType.(*types.Pointer); ok {
|
|
53
|
+
elemType := ptrTypeUnwrapped.Elem()
|
|
54
|
+
if elemType != nil {
|
|
55
|
+
// If it's a pointer to a struct, handle field access specially
|
|
56
|
+
if _, isStruct := elemType.Underlying().(*types.Struct); isStruct {
|
|
57
|
+
// Get the object for the pointer variable itself (e.g., 'p')
|
|
58
|
+
var ptrObj types.Object
|
|
59
|
+
if ptrIdent, isIdent := starExpr.X.(*ast.Ident); isIdent {
|
|
60
|
+
ptrObj = c.pkg.TypesInfo.ObjectOf(ptrIdent)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Write the pointer expression (e.g., p or p.value if p is boxed)
|
|
64
|
+
if err := c.WriteValueExpr(starExpr.X); err != nil {
|
|
65
|
+
return fmt.Errorf("failed to write pointer expression for (*p).field: %w", err)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Add ! for non-null assertion
|
|
69
|
+
c.tsw.WriteLiterally("!")
|
|
70
|
+
|
|
71
|
+
// Add .value ONLY if the pointer variable itself needs boxed access
|
|
72
|
+
// This handles the case where 'p' points to a boxed struct (e.g., p = s where s is Box<MyStruct>)
|
|
73
|
+
if ptrObj != nil && c.analysis.NeedsBoxedAccess(ptrObj) {
|
|
74
|
+
c.tsw.WriteLiterally(".value")
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Add .field
|
|
78
|
+
c.tsw.WriteLiterally(".")
|
|
79
|
+
c.WriteIdent(exp.Sel, false) // Don't add .value to the field itself
|
|
80
|
+
return nil
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// --- End Special Case ---
|
|
87
|
+
|
|
88
|
+
// Fallback / Normal Case (e.g., obj.Field, pkg.Var, method calls)
|
|
89
|
+
// WriteValueExpr handles adding .value for the base variable itself if it's boxed.
|
|
90
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
91
|
+
return fmt.Errorf("failed to write selector base expression: %w", err)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Add .
|
|
95
|
+
c.tsw.WriteLiterally(".")
|
|
96
|
+
|
|
97
|
+
// Write the field/method name.
|
|
98
|
+
// Pass 'true' to WriteIdent to potentially add '.value' if the field itself
|
|
99
|
+
// needs boxed access (e.g., accessing a primitive field via pointer where
|
|
100
|
+
// the field's address might have been taken elsewhere - less common but possible).
|
|
101
|
+
// For simple struct field access like p.Val or (*p).Val, WriteIdent(..., true)
|
|
102
|
+
// relies on NeedsBoxedAccess for the field 'Val', which should typically be false.
|
|
103
|
+
c.WriteIdent(exp.Sel, true)
|
|
104
|
+
return nil
|
|
105
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"go/ast"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// WriteStarExpr translates a Go pointer dereference expression (`ast.StarExpr`, e.g., `*p`)
|
|
9
|
+
// into its TypeScript equivalent. This involves careful handling of Go's pointers
|
|
10
|
+
// and TypeScript's boxing mechanism for emulating pointer semantics.
|
|
11
|
+
//
|
|
12
|
+
// The translation depends on whether the pointer variable `p` itself is boxed and
|
|
13
|
+
// what type of value it points to:
|
|
14
|
+
// 1. If `p` is not boxed and points to a primitive or another pointer: `*p` -> `p!.value`.
|
|
15
|
+
// (`p` holds a box, so dereference accesses its `value` field).
|
|
16
|
+
// 2. If `p` is not boxed and points to a struct: `*p` -> `p!`.
|
|
17
|
+
// (`p` holds the struct instance directly; structs are reference types in TS).
|
|
18
|
+
// 3. If `p` is boxed (i.e., `p` is `$.Box<PointerType>`) and points to a primitive/pointer:
|
|
19
|
+
// `*p` -> `p.value!.value`.
|
|
20
|
+
// (First `.value` unboxes `p`, then `!.value` dereferences the inner pointer).
|
|
21
|
+
// 4. If `p` is boxed and points to a struct: `*p` -> `p.value!`.
|
|
22
|
+
// (First `.value` unboxes `p` to get the struct instance).
|
|
23
|
+
//
|
|
24
|
+
// `WriteValueExpr(operand)` handles the initial unboxing of `p` if `p` itself is a boxed variable.
|
|
25
|
+
// A non-null assertion `!` is always added as pointers can be nil.
|
|
26
|
+
// `c.analysis.NeedsBoxedDeref(ptrType)` determines if an additional `.value` is needed
|
|
27
|
+
// based on whether the dereferenced type is a primitive/pointer (requires `.value`) or
|
|
28
|
+
// a struct (does not require `.value`).
|
|
29
|
+
func (c *GoToTSCompiler) WriteStarExpr(exp *ast.StarExpr) error {
|
|
30
|
+
// Generate code for a pointer dereference expression (*p).
|
|
31
|
+
//
|
|
32
|
+
// IMPORTANT: Pointer dereferencing in TypeScript requires careful handling of the box/unbox state:
|
|
33
|
+
//
|
|
34
|
+
// 1. p!.value - when p is not boxed and points to a primitive/pointer
|
|
35
|
+
// Example: let p = x (where x is a box) => p!.value
|
|
36
|
+
//
|
|
37
|
+
// 2. p! - when p is not boxed and points to a struct
|
|
38
|
+
// Example: let p = new MyStruct() => p! (structs are reference types)
|
|
39
|
+
//
|
|
40
|
+
// 3. p.value!.value - when p is boxed and points to a primitive/pointer
|
|
41
|
+
// Example: let p = $.box(x) (where x is another box) => p.value!.value
|
|
42
|
+
//
|
|
43
|
+
// 4. p.value! - when p is boxed and points to a struct
|
|
44
|
+
// Example: let p = $.box(new MyStruct()) => p.value!
|
|
45
|
+
//
|
|
46
|
+
// Critical bug fix: We must handle each case correctly to avoid over-dereferencing
|
|
47
|
+
// (adding too many .value) or under-dereferencing (missing .value where needed)
|
|
48
|
+
//
|
|
49
|
+
// NOTE: This logic aligns with design/BOXES_POINTERS.md.
|
|
50
|
+
|
|
51
|
+
// Get the operand expression and its type information
|
|
52
|
+
operand := exp.X
|
|
53
|
+
|
|
54
|
+
// Get the type of the operand (the pointer being dereferenced)
|
|
55
|
+
ptrType := c.pkg.TypesInfo.TypeOf(operand)
|
|
56
|
+
|
|
57
|
+
// Special case for handling multi-level dereferencing:
|
|
58
|
+
// Check if the operand is itself a StarExpr (e.g., **p or ***p)
|
|
59
|
+
// We need to handle these specially to correctly generate nested .value accesses
|
|
60
|
+
if starExpr, isStarExpr := operand.(*ast.StarExpr); isStarExpr {
|
|
61
|
+
// First, write the inner star expression
|
|
62
|
+
if err := c.WriteStarExpr(starExpr); err != nil {
|
|
63
|
+
return fmt.Errorf("failed to write inner star expression: %w", err)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Always add .value for multi-level dereferences
|
|
67
|
+
// For expressions like **p, each * adds a .value
|
|
68
|
+
c.tsw.WriteLiterally("!.value")
|
|
69
|
+
return nil
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Standard case: single-level dereference
|
|
73
|
+
// Write the pointer expression, which will access .value if the variable is boxed
|
|
74
|
+
// WriteValueExpr will add .value if the variable itself is boxed (p.value)
|
|
75
|
+
if err := c.WriteValueExpr(operand); err != nil {
|
|
76
|
+
return fmt.Errorf("failed to write star expression operand: %w", err)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Add ! for null assertion - all pointers can be null in TypeScript
|
|
80
|
+
c.tsw.WriteLiterally("!")
|
|
81
|
+
|
|
82
|
+
// Add .value only if we need boxed dereferencing for this type of pointer
|
|
83
|
+
// This depends on whether we're dereferencing to a primitive (needs .value)
|
|
84
|
+
// or to a struct (no .value needed)
|
|
85
|
+
if c.analysis.NeedsBoxedDeref(ptrType) {
|
|
86
|
+
c.tsw.WriteLiterally(".value")
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return nil
|
|
90
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"go/ast"
|
|
5
|
+
"go/types"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// WriteTypeExpr translates a Go abstract syntax tree (AST) expression (`ast.Expr`)
|
|
9
|
+
// that represents a type into its TypeScript type equivalent using type information.
|
|
10
|
+
//
|
|
11
|
+
// It handles various Go type expressions:
|
|
12
|
+
// - Basic types (e.g., int, string, bool) -> TypeScript primitives (number, string, boolean)
|
|
13
|
+
// - Named types -> TypeScript class/interface names
|
|
14
|
+
// - Pointer types (`*T`) -> `$.Box<T_ts> | null`
|
|
15
|
+
// - Slice types (`[]T`) -> `$.Slice<T_ts>`
|
|
16
|
+
// - Array types (`[N]T`) -> `T_ts[]`
|
|
17
|
+
// - Map types (`map[K]V`) -> `Map<K_ts, V_ts>`
|
|
18
|
+
// - Channel types (`chan T`) -> `$.Channel<T_ts>`
|
|
19
|
+
// - Struct types -> TypeScript object types or class names
|
|
20
|
+
// - Interface types -> TypeScript interface types or "any"
|
|
21
|
+
// - Function types -> TypeScript function signatures
|
|
22
|
+
func (c *GoToTSCompiler) WriteTypeExpr(a ast.Expr) {
|
|
23
|
+
// Get type information for the expression and use WriteGoType
|
|
24
|
+
typ := c.pkg.TypesInfo.TypeOf(a)
|
|
25
|
+
c.WriteGoType(typ)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// writeTypeDescription writes the TypeInfo for a type expr.
|
|
29
|
+
func (c *GoToTSCompiler) writeTypeDescription(typeExpr ast.Expr) {
|
|
30
|
+
switch t := typeExpr.(type) {
|
|
31
|
+
case *ast.Ident:
|
|
32
|
+
if isPrimitiveType(t.Name) {
|
|
33
|
+
if tsType, ok := GoBuiltinToTypescript(t.Name); ok {
|
|
34
|
+
c.tsw.WriteLiterally("{")
|
|
35
|
+
c.tsw.WriteLiterally("kind: $.TypeKind.Basic, ")
|
|
36
|
+
c.tsw.WriteLiterallyf("name: '%s'", tsType)
|
|
37
|
+
c.tsw.WriteLiterally("}")
|
|
38
|
+
} else {
|
|
39
|
+
// Fallback for other primitive types
|
|
40
|
+
c.tsw.WriteLiterally("{")
|
|
41
|
+
c.tsw.WriteLiterally("kind: $.TypeKind.Basic, ")
|
|
42
|
+
c.tsw.WriteLiterallyf("name: '%s'", t.Name)
|
|
43
|
+
c.tsw.WriteLiterally("}")
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
// For named types, just use the name string
|
|
47
|
+
c.tsw.WriteLiterallyf("'%s'", t.Name)
|
|
48
|
+
}
|
|
49
|
+
case *ast.SelectorExpr:
|
|
50
|
+
if ident, ok := t.X.(*ast.Ident); ok {
|
|
51
|
+
c.tsw.WriteLiterallyf("'%s.%s'", ident.Name, t.Sel.Name)
|
|
52
|
+
}
|
|
53
|
+
case *ast.ArrayType:
|
|
54
|
+
typeKind := "$.TypeKind.Slice"
|
|
55
|
+
if t.Len != nil {
|
|
56
|
+
typeKind = "$.TypeKind.Array"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
c.tsw.WriteLiterally("{")
|
|
60
|
+
c.tsw.WriteLiterallyf("kind: %s, ", typeKind)
|
|
61
|
+
c.tsw.WriteLiterally("elemType: ")
|
|
62
|
+
c.writeTypeDescription(t.Elt)
|
|
63
|
+
c.tsw.WriteLiterally("}")
|
|
64
|
+
case *ast.StructType:
|
|
65
|
+
c.tsw.WriteLiterally("{")
|
|
66
|
+
c.tsw.WriteLiterally("kind: $.TypeKind.Struct, ")
|
|
67
|
+
|
|
68
|
+
// Add field names and types to the struct type info
|
|
69
|
+
if t.Fields != nil && t.Fields.List != nil {
|
|
70
|
+
c.tsw.WriteLiterally("fields: {")
|
|
71
|
+
|
|
72
|
+
hasFields := false
|
|
73
|
+
for _, field := range t.Fields.List {
|
|
74
|
+
if len(field.Names) > 0 {
|
|
75
|
+
for _, name := range field.Names {
|
|
76
|
+
if hasFields {
|
|
77
|
+
c.tsw.WriteLiterally(", ")
|
|
78
|
+
}
|
|
79
|
+
c.tsw.WriteLiterallyf("'%s': ", name.Name)
|
|
80
|
+
c.writeTypeDescription(field.Type)
|
|
81
|
+
hasFields = true
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
c.tsw.WriteLiterally("}, ")
|
|
87
|
+
} else {
|
|
88
|
+
c.tsw.WriteLiterally("fields: {}, ")
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
c.tsw.WriteLiterally("methods: []")
|
|
92
|
+
|
|
93
|
+
c.tsw.WriteLiterally("}")
|
|
94
|
+
case *ast.MapType:
|
|
95
|
+
c.tsw.WriteLiterally("{")
|
|
96
|
+
c.tsw.WriteLiterally("kind: $.TypeKind.Map, ")
|
|
97
|
+
c.tsw.WriteLiterally("keyType: ")
|
|
98
|
+
c.writeTypeDescription(t.Key)
|
|
99
|
+
c.tsw.WriteLiterally(", ")
|
|
100
|
+
c.tsw.WriteLiterally("elemType: ")
|
|
101
|
+
c.writeTypeDescription(t.Value)
|
|
102
|
+
c.tsw.WriteLiterally("}")
|
|
103
|
+
case *ast.StarExpr:
|
|
104
|
+
c.tsw.WriteLiterally("{")
|
|
105
|
+
c.tsw.WriteLiterally("kind: $.TypeKind.Pointer, ")
|
|
106
|
+
c.tsw.WriteLiterally("elemType: ")
|
|
107
|
+
c.writeTypeDescription(t.X)
|
|
108
|
+
c.tsw.WriteLiterally("}")
|
|
109
|
+
case *ast.FuncType:
|
|
110
|
+
// For function types, create a type descriptor object with params and results
|
|
111
|
+
c.tsw.WriteLiterally("{")
|
|
112
|
+
c.tsw.WriteLiterally("kind: $.TypeKind.Function")
|
|
113
|
+
|
|
114
|
+
// Add name if this is a named function type
|
|
115
|
+
if namedType := c.pkg.TypesInfo.TypeOf(typeExpr); namedType != nil {
|
|
116
|
+
if named, ok := namedType.(*types.Named); ok {
|
|
117
|
+
c.tsw.WriteLiterallyf(", name: '%s'", named.Obj().Name())
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Add params if present
|
|
122
|
+
if t.Params != nil && len(t.Params.List) > 0 {
|
|
123
|
+
c.tsw.WriteLiterally(", params: [")
|
|
124
|
+
for i, param := range t.Params.List {
|
|
125
|
+
if i > 0 {
|
|
126
|
+
c.tsw.WriteLiterally(", ")
|
|
127
|
+
}
|
|
128
|
+
c.writeTypeDescription(param.Type)
|
|
129
|
+
}
|
|
130
|
+
c.tsw.WriteLiterally("]")
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Add results if present
|
|
134
|
+
if t.Results != nil && len(t.Results.List) > 0 {
|
|
135
|
+
c.tsw.WriteLiterally(", results: [")
|
|
136
|
+
for i, result := range t.Results.List {
|
|
137
|
+
if i > 0 {
|
|
138
|
+
c.tsw.WriteLiterally(", ")
|
|
139
|
+
}
|
|
140
|
+
c.writeTypeDescription(result.Type)
|
|
141
|
+
}
|
|
142
|
+
c.tsw.WriteLiterally("]")
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
c.tsw.WriteLiterally("}")
|
|
146
|
+
return
|
|
147
|
+
case *ast.ChanType:
|
|
148
|
+
c.tsw.WriteLiterally("{")
|
|
149
|
+
c.tsw.WriteLiterally("kind: $.TypeKind.Channel, ")
|
|
150
|
+
c.tsw.WriteLiterally("elemType: ")
|
|
151
|
+
|
|
152
|
+
// Add element type
|
|
153
|
+
if ident, ok := t.Value.(*ast.Ident); ok && isPrimitiveType(ident.Name) {
|
|
154
|
+
if tsType, ok := GoBuiltinToTypescript(ident.Name); ok {
|
|
155
|
+
c.tsw.WriteLiterallyf("'%s'", tsType)
|
|
156
|
+
} else {
|
|
157
|
+
c.tsw.WriteLiterallyf("'%s'", ident.Name) // Fallback
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
c.writeTypeDescription(t.Value)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Add direction
|
|
164
|
+
c.tsw.WriteLiterally(", direction: ")
|
|
165
|
+
switch t.Dir {
|
|
166
|
+
case ast.SEND:
|
|
167
|
+
c.tsw.WriteLiterally("'send'")
|
|
168
|
+
case ast.RECV:
|
|
169
|
+
c.tsw.WriteLiterally("'receive'")
|
|
170
|
+
case ast.SEND | ast.RECV: // bidirectional
|
|
171
|
+
c.tsw.WriteLiterally("'both'")
|
|
172
|
+
default:
|
|
173
|
+
// This should not happen, but just in case
|
|
174
|
+
c.tsw.WriteLiterally("'both'")
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
c.tsw.WriteLiterally("}")
|
|
178
|
+
default:
|
|
179
|
+
// For other types, use the string representation
|
|
180
|
+
c.tsw.WriteLiterallyf("'%s'", c.getTypeNameString(typeExpr))
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import "go/ast"
|
|
4
|
+
|
|
5
|
+
// WriteValueExpr translates a Go abstract syntax tree (AST) expression (`ast.Expr`)
|
|
6
|
+
// that represents a value into its TypeScript value equivalent.
|
|
7
|
+
// This is a central dispatch function for various expression types:
|
|
8
|
+
// - Identifiers (`ast.Ident`): Delegates to `WriteIdent`, potentially adding `.value` for boxed variables.
|
|
9
|
+
// - Selector expressions (`ast.SelectorExpr`, e.g., `obj.Field` or `pkg.Var`): Delegates to `WriteSelectorExpr`.
|
|
10
|
+
// - Pointer dereferences (`ast.StarExpr`, e.g., `*ptr`): Delegates to `WriteStarExpr`.
|
|
11
|
+
// - Function calls (`ast.CallExpr`): Delegates to `WriteCallExpr`.
|
|
12
|
+
// - Unary operations (`ast.UnaryExpr`, e.g., `!cond`, `&val`): Delegates to `WriteUnaryExpr`.
|
|
13
|
+
// - Binary operations (`ast.BinaryExpr`, e.g., `a + b`): Delegates to `WriteBinaryExpr`.
|
|
14
|
+
// - Basic literals (`ast.BasicLit`, e.g., `123`, `"hello"`): Delegates to `WriteBasicLit`.
|
|
15
|
+
// - Composite literals (`ast.CompositeLit`, e.g., `MyStruct{}`): Delegates to `WriteCompositeLit`.
|
|
16
|
+
// - Key-value expressions (`ast.KeyValueExpr`): Delegates to `WriteKeyValueExpr`.
|
|
17
|
+
// - Type assertions in expression context (`ast.TypeAssertExpr`, e.g., `val.(Type)`): Delegates to `WriteTypeAssertExpr`.
|
|
18
|
+
// - Index expressions (`ast.IndexExpr`):
|
|
19
|
+
// - For maps: `myMap[key]` becomes `myMap.get(key) ?? zeroValue`.
|
|
20
|
+
// - For arrays/slices: `myArray[idx]` becomes `myArray![idx]`.
|
|
21
|
+
//
|
|
22
|
+
// - Slice expressions (`ast.SliceExpr`, e.g., `s[low:high:max]`): Translates to `$.slice(s, low, high, max)`.
|
|
23
|
+
// - Parenthesized expressions (`ast.ParenExpr`): Translates `(X)` to `(X)`.
|
|
24
|
+
// - Function literals (`ast.FuncLit`): Delegates to `WriteFuncLitValue`.
|
|
25
|
+
// Unhandled value expressions result in a comment.
|
|
26
|
+
func (c *GoToTSCompiler) WriteValueExpr(a ast.Expr) error {
|
|
27
|
+
switch exp := a.(type) {
|
|
28
|
+
case *ast.Ident:
|
|
29
|
+
c.WriteIdent(exp, true) // adds .value accessor
|
|
30
|
+
return nil
|
|
31
|
+
case *ast.SelectorExpr:
|
|
32
|
+
return c.WriteSelectorExpr(exp)
|
|
33
|
+
case *ast.StarExpr:
|
|
34
|
+
return c.WriteStarExpr(exp)
|
|
35
|
+
case *ast.CallExpr:
|
|
36
|
+
return c.WriteCallExpr(exp)
|
|
37
|
+
case *ast.UnaryExpr:
|
|
38
|
+
return c.WriteUnaryExpr(exp)
|
|
39
|
+
case *ast.BinaryExpr:
|
|
40
|
+
return c.WriteBinaryExpr(exp)
|
|
41
|
+
case *ast.BasicLit:
|
|
42
|
+
c.WriteBasicLit(exp)
|
|
43
|
+
return nil
|
|
44
|
+
case *ast.CompositeLit:
|
|
45
|
+
return c.WriteCompositeLit(exp)
|
|
46
|
+
case *ast.KeyValueExpr:
|
|
47
|
+
return c.WriteKeyValueExpr(exp)
|
|
48
|
+
case *ast.TypeAssertExpr:
|
|
49
|
+
// Handle type assertion in an expression context
|
|
50
|
+
return c.WriteTypeAssertExpr(exp)
|
|
51
|
+
case *ast.IndexExpr:
|
|
52
|
+
return c.WriteIndexExpr(exp)
|
|
53
|
+
case *ast.SliceExpr:
|
|
54
|
+
// Translate Go slice expression to $.goSlice(x, low, high, max)
|
|
55
|
+
c.tsw.WriteLiterally("$.goSlice(")
|
|
56
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
57
|
+
return err
|
|
58
|
+
}
|
|
59
|
+
// low argument
|
|
60
|
+
c.tsw.WriteLiterally(", ")
|
|
61
|
+
if exp.Low != nil {
|
|
62
|
+
if err := c.WriteValueExpr(exp.Low); err != nil {
|
|
63
|
+
return err
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
c.tsw.WriteLiterally("undefined")
|
|
67
|
+
}
|
|
68
|
+
// high argument
|
|
69
|
+
c.tsw.WriteLiterally(", ")
|
|
70
|
+
if exp.High != nil {
|
|
71
|
+
if err := c.WriteValueExpr(exp.High); err != nil {
|
|
72
|
+
return err
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
c.tsw.WriteLiterally("undefined")
|
|
76
|
+
}
|
|
77
|
+
// max argument (only for full slice expressions)
|
|
78
|
+
if exp.Slice3 {
|
|
79
|
+
c.tsw.WriteLiterally(", ")
|
|
80
|
+
if exp.Max != nil {
|
|
81
|
+
if err := c.WriteValueExpr(exp.Max); err != nil {
|
|
82
|
+
return err
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
c.tsw.WriteLiterally("undefined")
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
c.tsw.WriteLiterally(")")
|
|
89
|
+
return nil
|
|
90
|
+
case *ast.ParenExpr:
|
|
91
|
+
// Check if this is a nil pointer to struct type cast: (*struct{})(nil)
|
|
92
|
+
if starExpr, isStarExpr := exp.X.(*ast.StarExpr); isStarExpr {
|
|
93
|
+
if _, isStructType := starExpr.X.(*ast.StructType); isStructType {
|
|
94
|
+
c.tsw.WriteLiterally("null")
|
|
95
|
+
return nil
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Check if this is a type cast with nil: (SomeType)(nil)
|
|
100
|
+
if ident, isIdent := exp.X.(*ast.Ident); isIdent && ident.Name == "nil" {
|
|
101
|
+
c.tsw.WriteLiterally("null")
|
|
102
|
+
return nil
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Translate (X) to (X)
|
|
106
|
+
// If we haven't written anything in this statement yet, prepend ;
|
|
107
|
+
c.tsw.WriteLiterally("(")
|
|
108
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
109
|
+
return err
|
|
110
|
+
}
|
|
111
|
+
c.tsw.WriteLiterally(")")
|
|
112
|
+
return nil
|
|
113
|
+
case *ast.FuncLit:
|
|
114
|
+
return c.WriteFuncLitValue(exp)
|
|
115
|
+
default:
|
|
116
|
+
c.tsw.WriteCommentLinef("unhandled value expr: %T", exp)
|
|
117
|
+
return nil
|
|
118
|
+
}
|
|
119
|
+
}
|