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
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import "go/token"
|
|
4
|
+
|
|
5
|
+
// goToTypescriptPrimitives maps Go built-in primitive type names (as strings)
|
|
6
|
+
// to their corresponding TypeScript type names. This map is used by
|
|
7
|
+
// `GoBuiltinToTypescript` for direct type name translation.
|
|
8
|
+
//
|
|
9
|
+
// Key mappings include:
|
|
10
|
+
// - `bool` -> `boolean`
|
|
11
|
+
// - `string` -> `string`
|
|
12
|
+
// - `int`, `int8`, `int16`, `int32`, `rune` (alias for int32) -> `number`
|
|
13
|
+
// - `uint`, `uint8` (`byte`), `uint16`, `uint32` -> `number`
|
|
14
|
+
// - `int64`, `uint64` -> `bigint` (requires ES2020+ TypeScript target)
|
|
15
|
+
// - `float32`, `float64` -> `number`
|
|
16
|
+
//
|
|
17
|
+
// This mapping assumes a target environment similar to GOOS=js, GOARCH=wasm,
|
|
18
|
+
// where Go's `int` and `uint` are 32-bit and fit within TypeScript's `number`.
|
|
19
|
+
var goToTypescriptPrimitives = map[string]string{
|
|
20
|
+
// Boolean
|
|
21
|
+
"bool": "boolean",
|
|
22
|
+
|
|
23
|
+
// Strings
|
|
24
|
+
"string": "string",
|
|
25
|
+
|
|
26
|
+
// Signed Integers
|
|
27
|
+
"int": "number",
|
|
28
|
+
"int8": "number",
|
|
29
|
+
"int16": "number",
|
|
30
|
+
"int32": "number",
|
|
31
|
+
"rune": "number", // alias for int32
|
|
32
|
+
"int64": "bigint", // Requires TypeScript target >= ES2020
|
|
33
|
+
|
|
34
|
+
// Unsigned Integers
|
|
35
|
+
"uint": "number",
|
|
36
|
+
"uint8": "number", // byte is an alias for uint8
|
|
37
|
+
"byte": "number",
|
|
38
|
+
"uint16": "number",
|
|
39
|
+
"uint32": "number",
|
|
40
|
+
"uint64": "bigint", // Requires TypeScript target >= ES2020
|
|
41
|
+
|
|
42
|
+
// Floating Point Numbers
|
|
43
|
+
"float32": "number",
|
|
44
|
+
"float64": "number",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
func isPrimitiveType(name string) bool {
|
|
48
|
+
_, ok := goToTypescriptPrimitives[name]
|
|
49
|
+
return ok
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// GoBuiltinToTypescript translates a Go built-in primitive type name (string)
|
|
53
|
+
// to its TypeScript equivalent. It uses the `goToTypescriptPrimitives` map
|
|
54
|
+
// for the conversion.
|
|
55
|
+
// It returns the TypeScript type name and `true` if the Go type name is found
|
|
56
|
+
// in the map. Otherwise, it returns an empty string and `false`.
|
|
57
|
+
// This function only handles primitive types listed in the map; composite types
|
|
58
|
+
// or custom types are not processed here.
|
|
59
|
+
func GoBuiltinToTypescript(typeName string) (string, bool) {
|
|
60
|
+
val, ok := goToTypescriptPrimitives[typeName]
|
|
61
|
+
return val, ok
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// tokenMap provides a mapping from Go `token.Token` types (representing operators
|
|
65
|
+
// and punctuation) to their corresponding string representations in TypeScript.
|
|
66
|
+
// This map is used by `TokenToTs` to translate Go operators during expression
|
|
67
|
+
// and statement compilation.
|
|
68
|
+
//
|
|
69
|
+
// Examples:
|
|
70
|
+
// - `token.ADD` (Go `+`) -> `"+"` (TypeScript `+`)
|
|
71
|
+
// - `token.LAND` (Go `&&`) -> `"&&"` (TypeScript `&&`)
|
|
72
|
+
// - `token.ASSIGN` (Go `=`) -> `"="` (TypeScript `=`)
|
|
73
|
+
// - `token.DEFINE` (Go `:=`) -> `"="` (TypeScript `=`, as `let` is handled separately)
|
|
74
|
+
//
|
|
75
|
+
// Some tokens like `token.ARROW` (channel send/receive) are handled specially
|
|
76
|
+
// in their respective expression/statement writers and might not be directly mapped here.
|
|
77
|
+
// Bitwise AND NOT (`&^=`) is also mapped but may require specific runtime support if not directly translatable.
|
|
78
|
+
var tokenMap = map[token.Token]string{
|
|
79
|
+
token.ADD: "+",
|
|
80
|
+
token.SUB: "-",
|
|
81
|
+
token.MUL: "*",
|
|
82
|
+
token.QUO: "/",
|
|
83
|
+
token.REM: "%",
|
|
84
|
+
token.AND: "&",
|
|
85
|
+
token.OR: "|",
|
|
86
|
+
token.XOR: "^",
|
|
87
|
+
token.SHL: "<<",
|
|
88
|
+
token.SHR: ">>",
|
|
89
|
+
|
|
90
|
+
token.ADD_ASSIGN: "+=",
|
|
91
|
+
token.SUB_ASSIGN: "-=",
|
|
92
|
+
token.MUL_ASSIGN: "*=",
|
|
93
|
+
token.QUO_ASSIGN: "/=",
|
|
94
|
+
token.REM_ASSIGN: "%=",
|
|
95
|
+
|
|
96
|
+
token.AND_ASSIGN: "&=",
|
|
97
|
+
token.OR_ASSIGN: "|=",
|
|
98
|
+
token.XOR_ASSIGN: "^=", // TODO: check if this works
|
|
99
|
+
token.SHL_ASSIGN: "<<=",
|
|
100
|
+
token.SHR_ASSIGN: ">>=",
|
|
101
|
+
token.AND_NOT_ASSIGN: "&^=",
|
|
102
|
+
|
|
103
|
+
token.LAND: "&&",
|
|
104
|
+
token.LOR: "||",
|
|
105
|
+
// token.ARROW: ""
|
|
106
|
+
token.INC: "++",
|
|
107
|
+
token.DEC: "--",
|
|
108
|
+
token.EQL: "==",
|
|
109
|
+
token.LSS: "<",
|
|
110
|
+
token.GTR: ">",
|
|
111
|
+
token.ASSIGN: "=",
|
|
112
|
+
token.NOT: "!",
|
|
113
|
+
|
|
114
|
+
token.NEQ: "!=",
|
|
115
|
+
token.LEQ: "<=",
|
|
116
|
+
token.GEQ: ">=",
|
|
117
|
+
token.DEFINE: "=", // :=
|
|
118
|
+
token.ELLIPSIS: "...", // TODO
|
|
119
|
+
|
|
120
|
+
token.LPAREN: "(",
|
|
121
|
+
token.LBRACK: "[",
|
|
122
|
+
token.LBRACE: "{",
|
|
123
|
+
token.COMMA: ",",
|
|
124
|
+
token.PERIOD: ".",
|
|
125
|
+
|
|
126
|
+
token.RPAREN: ")",
|
|
127
|
+
token.RBRACK: "]",
|
|
128
|
+
token.RBRACE: "}",
|
|
129
|
+
token.SEMICOLON: ";",
|
|
130
|
+
token.COLON: ":",
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// TokenToTs converts a Go `token.Token` (representing an operator or punctuation)
|
|
134
|
+
// into its corresponding TypeScript string representation using the `tokenMap`.
|
|
135
|
+
// It returns the TypeScript string and `true` if the token is found in the map.
|
|
136
|
+
// Otherwise, it returns an empty string and `false`. This function is essential
|
|
137
|
+
// for translating expressions involving operators (e.g., arithmetic, logical,
|
|
138
|
+
// assignment operators).
|
|
139
|
+
func TokenToTs(tok token.Token) (string, bool) {
|
|
140
|
+
t, ok := tokenMap[tok]
|
|
141
|
+
return t, ok
|
|
142
|
+
}
|
|
@@ -5,153 +5,19 @@ import (
|
|
|
5
5
|
"go/ast"
|
|
6
6
|
"go/types"
|
|
7
7
|
"strings"
|
|
8
|
-
|
|
9
|
-
// types provides type information for Go types.
|
|
10
|
-
"github.com/pkg/errors"
|
|
11
8
|
)
|
|
12
9
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if dotIndex := strings.LastIndex(fieldKeyName, "."); dotIndex != -1 {
|
|
25
|
-
fieldKeyName = fieldKeyName[dotIndex+1:]
|
|
26
|
-
}
|
|
27
|
-
return fieldKeyName
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
func (c *GoToTSCompiler) writeGetterSetter(fieldName string, fieldType types.Type, doc, comment *ast.CommentGroup) {
|
|
32
|
-
fieldTypeStr := c.getTypeString(fieldType)
|
|
33
|
-
|
|
34
|
-
// Generate getter
|
|
35
|
-
if doc != nil {
|
|
36
|
-
c.WriteDoc(doc)
|
|
37
|
-
}
|
|
38
|
-
if comment != nil {
|
|
39
|
-
c.WriteDoc(comment)
|
|
40
|
-
}
|
|
41
|
-
c.tsw.WriteLinef("public get %s(): %s {", fieldName, fieldTypeStr)
|
|
42
|
-
c.tsw.Indent(1)
|
|
43
|
-
c.tsw.WriteLinef("return this._fields.%s.value", fieldName)
|
|
44
|
-
c.tsw.Indent(-1)
|
|
45
|
-
c.tsw.WriteLine("}")
|
|
46
|
-
|
|
47
|
-
// Generate setter (no comments)
|
|
48
|
-
c.tsw.WriteLinef("public set %s(value: %s) {", fieldName, fieldTypeStr)
|
|
49
|
-
c.tsw.Indent(1)
|
|
50
|
-
c.tsw.WriteLinef("this._fields.%s.value = value", fieldName)
|
|
51
|
-
c.tsw.Indent(-1)
|
|
52
|
-
c.tsw.WriteLine("}")
|
|
53
|
-
c.tsw.WriteLine("")
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
func (c *GoToTSCompiler) writeBoxedFieldInitializer(fieldName string, fieldType types.Type, isEmbedded bool) {
|
|
57
|
-
c.tsw.WriteLiterally(fieldName)
|
|
58
|
-
c.tsw.WriteLiterally(": $.box(")
|
|
59
|
-
|
|
60
|
-
if isEmbedded {
|
|
61
|
-
if _, isPtr := fieldType.(*types.Pointer); isPtr {
|
|
62
|
-
c.tsw.WriteLiterallyf("init?.%s ?? null", fieldName)
|
|
63
|
-
} else {
|
|
64
|
-
typeForNew := fieldName
|
|
65
|
-
c.tsw.WriteLiterallyf("new %s(init?.%s)", typeForNew, fieldName)
|
|
66
|
-
}
|
|
67
|
-
} else {
|
|
68
|
-
isStructValueType := false
|
|
69
|
-
var structTypeNameForClone string
|
|
70
|
-
if named, ok := fieldType.(*types.Named); ok {
|
|
71
|
-
if _, isStruct := named.Underlying().(*types.Struct); isStruct {
|
|
72
|
-
isStructValueType = true
|
|
73
|
-
structTypeNameForClone = named.Obj().Name()
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if isStructValueType {
|
|
78
|
-
c.tsw.WriteLiterallyf("init?.%s?.clone() ?? new %s()", fieldName, structTypeNameForClone)
|
|
79
|
-
} else {
|
|
80
|
-
c.tsw.WriteLiterallyf("init?.%s ?? ", fieldName)
|
|
81
|
-
c.WriteZeroValueForType(fieldType)
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
c.tsw.WriteLiterally(")")
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
func (c *GoToTSCompiler) writeClonedFieldInitializer(fieldName string, fieldType types.Type, isEmbedded bool) {
|
|
89
|
-
c.tsw.WriteLiterally(fieldName)
|
|
90
|
-
c.tsw.WriteLiterally(": $.box(")
|
|
91
|
-
|
|
92
|
-
if isEmbedded {
|
|
93
|
-
isPointerToStruct := false
|
|
94
|
-
trueType := fieldType
|
|
95
|
-
if ptr, isPtr := trueType.(*types.Pointer); isPtr {
|
|
96
|
-
trueType = ptr.Elem()
|
|
97
|
-
isPointerToStruct = true
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if named, isNamed := trueType.(*types.Named); isNamed {
|
|
101
|
-
_, isUnderlyingStruct := named.Underlying().(*types.Struct)
|
|
102
|
-
if isUnderlyingStruct && !isPointerToStruct { // Is a value struct
|
|
103
|
-
c.tsw.WriteLiterallyf("this._fields.%s.value.clone()", fieldName)
|
|
104
|
-
} else { // Is a pointer to a struct, or not a struct
|
|
105
|
-
c.tsw.WriteLiterallyf("this._fields.%s.value", fieldName)
|
|
106
|
-
}
|
|
107
|
-
} else {
|
|
108
|
-
c.tsw.WriteLiterallyf("this._fields.%s.value", fieldName)
|
|
109
|
-
}
|
|
110
|
-
} else {
|
|
111
|
-
isValueTypeStruct := false
|
|
112
|
-
if named, ok := fieldType.(*types.Named); ok {
|
|
113
|
-
if _, isStruct := named.Underlying().(*types.Struct); isStruct {
|
|
114
|
-
isValueTypeStruct = true
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if isValueTypeStruct {
|
|
119
|
-
c.tsw.WriteLiterallyf("this._fields.%s.value?.clone() ?? null", fieldName)
|
|
120
|
-
} else {
|
|
121
|
-
c.tsw.WriteLiterallyf("this._fields.%s.value", fieldName)
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
c.tsw.WriteLiterally(")")
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// WriteTypeSpec writes the type specification to the output.
|
|
129
|
-
func (c *GoToTSCompiler) WriteTypeSpec(a *ast.TypeSpec) error {
|
|
130
|
-
if a.Doc != nil {
|
|
131
|
-
c.WriteDoc(a.Doc)
|
|
132
|
-
}
|
|
133
|
-
if a.Comment != nil {
|
|
134
|
-
c.WriteDoc(a.Comment)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
switch t := a.Type.(type) {
|
|
138
|
-
case *ast.StructType:
|
|
139
|
-
return c.WriteStructTypeSpec(a, t)
|
|
140
|
-
case *ast.InterfaceType:
|
|
141
|
-
return c.WriteInterfaceTypeSpec(a, t)
|
|
142
|
-
default:
|
|
143
|
-
// type alias
|
|
144
|
-
c.tsw.WriteLiterally("type ")
|
|
145
|
-
if err := c.WriteValueExpr(a.Name); err != nil {
|
|
146
|
-
return err
|
|
147
|
-
}
|
|
148
|
-
c.tsw.WriteLiterally(" = ")
|
|
149
|
-
c.WriteTypeExpr(a.Type) // The aliased type
|
|
150
|
-
c.tsw.WriteLine(";")
|
|
151
|
-
}
|
|
152
|
-
return nil
|
|
153
|
-
}
|
|
154
|
-
|
|
10
|
+
// WriteStructTypeSpec generates the TypeScript class definition for a Go struct type.
|
|
11
|
+
// It handles the generation of:
|
|
12
|
+
// - The class declaration.
|
|
13
|
+
// - Getters and setters for all fields (both direct and embedded).
|
|
14
|
+
// - The internal `_fields` property, which stores field values in `$.Box` containers
|
|
15
|
+
// to maintain Go's value semantics.
|
|
16
|
+
// - A constructor that initializes the `_fields` and allows partial initialization.
|
|
17
|
+
// - A `clone` method for creating a deep copy of the struct instance.
|
|
18
|
+
// - Methods defined directly on the struct.
|
|
19
|
+
// - Wrapper methods for promoted fields and methods from embedded structs,
|
|
20
|
+
// ensuring correct access and behavior.
|
|
155
21
|
func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType) error {
|
|
156
22
|
c.tsw.WriteLiterally("class ")
|
|
157
23
|
if err := c.WriteValueExpr(a.Name); err != nil {
|
|
@@ -188,7 +54,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
188
54
|
}
|
|
189
55
|
|
|
190
56
|
// Generate getters and setters for EMBEDDED struct fields themselves
|
|
191
|
-
for i :=
|
|
57
|
+
for i := range underlyingStruct.NumFields() {
|
|
192
58
|
field := underlyingStruct.Field(i)
|
|
193
59
|
if field.Anonymous() {
|
|
194
60
|
fieldKeyName := c.getEmbeddedFieldKeyName(field.Type())
|
|
@@ -221,31 +87,35 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
221
87
|
c.tsw.WriteLine("")
|
|
222
88
|
c.tsw.WriteLinef("constructor(init?: Partial<%s>) {", flattenedInitType)
|
|
223
89
|
c.tsw.Indent(1)
|
|
224
|
-
c.tsw.
|
|
225
|
-
c.tsw.Indent(1)
|
|
90
|
+
c.tsw.WriteLiterally("this._fields = {")
|
|
226
91
|
|
|
227
92
|
numFields := underlyingStruct.NumFields()
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
fieldKeyName
|
|
236
|
-
|
|
93
|
+
if numFields != 0 {
|
|
94
|
+
c.tsw.WriteLine("")
|
|
95
|
+
c.tsw.Indent(1)
|
|
96
|
+
|
|
97
|
+
for i := range numFields {
|
|
98
|
+
field := underlyingStruct.Field(i)
|
|
99
|
+
fieldType := field.Type()
|
|
100
|
+
var fieldKeyName string
|
|
101
|
+
if field.Anonymous() {
|
|
102
|
+
fieldKeyName = c.getEmbeddedFieldKeyName(field.Type())
|
|
103
|
+
} else {
|
|
104
|
+
fieldKeyName = field.Name()
|
|
105
|
+
}
|
|
237
106
|
|
|
238
|
-
|
|
107
|
+
c.writeBoxedFieldInitializer(fieldKeyName, fieldType, field.Anonymous())
|
|
239
108
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
109
|
+
if i < numFields-1 {
|
|
110
|
+
c.tsw.WriteLine(",")
|
|
111
|
+
} else {
|
|
112
|
+
c.tsw.WriteLine("")
|
|
113
|
+
}
|
|
244
114
|
}
|
|
115
|
+
c.tsw.Indent(-1)
|
|
245
116
|
}
|
|
246
|
-
|
|
247
|
-
c.tsw.Indent(-1)
|
|
248
117
|
c.tsw.WriteLine("}")
|
|
118
|
+
|
|
249
119
|
c.tsw.Indent(-1)
|
|
250
120
|
c.tsw.WriteLine("}")
|
|
251
121
|
c.tsw.WriteLine("")
|
|
@@ -257,7 +127,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
257
127
|
c.tsw.WriteLine("cloned._fields = {")
|
|
258
128
|
c.tsw.Indent(1)
|
|
259
129
|
|
|
260
|
-
for i :=
|
|
130
|
+
for i := range numFields {
|
|
261
131
|
field := underlyingStruct.Field(i)
|
|
262
132
|
fieldType := field.Type()
|
|
263
133
|
var fieldKeyName string
|
|
@@ -307,7 +177,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
307
177
|
seenPromotedFields := make(map[string]bool)
|
|
308
178
|
directMethods := make(map[string]bool)
|
|
309
179
|
// Populate directMethods (methods defined directly on this struct type)
|
|
310
|
-
for i :=
|
|
180
|
+
for i := range goStructType.NumMethods() {
|
|
311
181
|
method := goStructType.Method(i)
|
|
312
182
|
sig := method.Type().(*types.Signature)
|
|
313
183
|
if sig.Recv() != nil {
|
|
@@ -322,7 +192,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
322
192
|
}
|
|
323
193
|
}
|
|
324
194
|
|
|
325
|
-
for i :=
|
|
195
|
+
for i := range underlyingStruct.NumFields() {
|
|
326
196
|
field := underlyingStruct.Field(i)
|
|
327
197
|
if !field.Anonymous() {
|
|
328
198
|
continue
|
|
@@ -383,7 +253,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
383
253
|
|
|
384
254
|
// Promoted methods
|
|
385
255
|
embeddedMethodSet := types.NewMethodSet(embeddedFieldType) // Use original field type for method set
|
|
386
|
-
for k :=
|
|
256
|
+
for k := range embeddedMethodSet.Len() {
|
|
387
257
|
methodSelection := embeddedMethodSet.At(k)
|
|
388
258
|
method := methodSelection.Obj().(*types.Func)
|
|
389
259
|
methodName := method.Name()
|
|
@@ -456,51 +326,57 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
456
326
|
}
|
|
457
327
|
}
|
|
458
328
|
|
|
459
|
-
// Add code to register the type with the runtime system
|
|
329
|
+
// Add code to register the type with the runtime type system
|
|
460
330
|
c.tsw.WriteLine("")
|
|
461
331
|
c.tsw.WriteLinef("// Register this type with the runtime type system")
|
|
462
332
|
c.tsw.WriteLinef("static __typeInfo = $.registerStructType(")
|
|
463
333
|
c.tsw.WriteLinef(" '%s',", className)
|
|
464
334
|
c.tsw.WriteLinef(" new %s(),", className)
|
|
465
|
-
c.tsw.
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
c.tsw.WriteLiterally(" = ")
|
|
481
|
-
// Get the types.Interface from the ast.InterfaceType.
|
|
482
|
-
// For an interface definition like `type MyInterface interface { M() }`,
|
|
483
|
-
// 't' is the *ast.InterfaceType representing `interface { M() }`.
|
|
484
|
-
// TypesInfo.TypeOf(t) will give the *types.Interface.
|
|
485
|
-
goType := c.pkg.TypesInfo.TypeOf(t)
|
|
486
|
-
if goType == nil {
|
|
487
|
-
return errors.Errorf("could not get type for interface AST node for %s", a.Name.Name)
|
|
488
|
-
}
|
|
489
|
-
ifaceType, ok := goType.(*types.Interface)
|
|
490
|
-
if !ok {
|
|
491
|
-
return errors.Errorf("expected *types.Interface, got %T for %s when processing interface literal", goType, a.Name.Name)
|
|
335
|
+
c.tsw.WriteLiterally(" [")
|
|
336
|
+
// Collect methods for the struct type
|
|
337
|
+
var structMethods []*types.Func
|
|
338
|
+
for i := range goStructType.NumMethods() {
|
|
339
|
+
method := goStructType.Method(i)
|
|
340
|
+
// Ensure it's a method directly on this type (not promoted here, promotion handled separately)
|
|
341
|
+
// Check if receiver is *T or T where T is goStructType
|
|
342
|
+
sig := method.Type().(*types.Signature)
|
|
343
|
+
recv := sig.Recv().Type()
|
|
344
|
+
if ptr, ok := recv.(*types.Pointer); ok {
|
|
345
|
+
recv = ptr.Elem()
|
|
346
|
+
}
|
|
347
|
+
if namedRecv, ok := recv.(*types.Named); ok && namedRecv.Obj() == goStructType.Obj() {
|
|
348
|
+
structMethods = append(structMethods, method)
|
|
349
|
+
}
|
|
492
350
|
}
|
|
493
|
-
c.
|
|
351
|
+
c.writeMethodSignatures(structMethods)
|
|
352
|
+
c.tsw.WriteLiterally("],")
|
|
494
353
|
c.tsw.WriteLine("")
|
|
495
354
|
|
|
496
|
-
|
|
497
|
-
|
|
355
|
+
c.tsw.WriteLinef(" %s,", className)
|
|
356
|
+
// Add field type information for type assertions
|
|
357
|
+
c.tsw.WriteLiterally(" {")
|
|
358
|
+
firstField := true
|
|
359
|
+
for i := 0; i < underlyingStruct.NumFields(); i++ {
|
|
360
|
+
field := underlyingStruct.Field(i)
|
|
361
|
+
var fieldKeyName string
|
|
362
|
+
if field.Anonymous() {
|
|
363
|
+
fieldKeyName = c.getEmbeddedFieldKeyName(field.Type())
|
|
364
|
+
} else {
|
|
365
|
+
fieldKeyName = field.Name()
|
|
366
|
+
}
|
|
367
|
+
// fieldTsType := c.getTypeString(field.Type())
|
|
368
|
+
if !firstField {
|
|
369
|
+
c.tsw.WriteLiterally(", ")
|
|
370
|
+
}
|
|
371
|
+
firstField = false
|
|
372
|
+
c.tsw.WriteLiterallyf("%q: ", fieldKeyName)
|
|
373
|
+
c.writeTypeInfoObject(field.Type()) // Use writeTypeInfoObject for field types
|
|
374
|
+
}
|
|
375
|
+
c.tsw.WriteLiterally("}")
|
|
498
376
|
c.tsw.WriteLine("")
|
|
499
|
-
c.tsw.WriteLinef("$.registerInterfaceType(")
|
|
500
|
-
c.tsw.WriteLinef(" '%s',", interfaceName)
|
|
501
|
-
c.tsw.WriteLinef(" null, // Zero value for interface is null")
|
|
502
|
-
c.tsw.WriteLinef(" new Set([%s]),", c.collectInterfaceMethods(t))
|
|
503
377
|
c.tsw.WriteLinef(");")
|
|
504
378
|
|
|
379
|
+
c.tsw.Indent(-1)
|
|
380
|
+
c.tsw.WriteLine("}")
|
|
505
381
|
return nil
|
|
506
382
|
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"go/ast"
|
|
5
|
+
"go/token"
|
|
6
|
+
|
|
7
|
+
"github.com/pkg/errors"
|
|
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 errors.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
|
+
// Start declaration
|
|
54
|
+
c.tsw.WriteLiterally("let ")
|
|
55
|
+
c.tsw.WriteLiterally(name.Name)
|
|
56
|
+
c.tsw.WriteLiterally(": ")
|
|
57
|
+
|
|
58
|
+
// Write type annotation
|
|
59
|
+
if needsBox {
|
|
60
|
+
// If boxed, the variable holds Box<OriginalGoType>
|
|
61
|
+
c.tsw.WriteLiterally("$.Box<")
|
|
62
|
+
c.WriteGoType(goType) // Write the original Go type T
|
|
63
|
+
c.tsw.WriteLiterally(">")
|
|
64
|
+
} else {
|
|
65
|
+
// If not boxed, the variable holds the translated Go type directly
|
|
66
|
+
c.WriteGoType(goType)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Write initializer
|
|
70
|
+
c.tsw.WriteLiterally(" = ")
|
|
71
|
+
hasInitializer := len(a.Values) > 0
|
|
72
|
+
var initializerExpr ast.Expr
|
|
73
|
+
if hasInitializer {
|
|
74
|
+
initializerExpr = a.Values[0]
|
|
75
|
+
|
|
76
|
+
// Special case for nil pointer to struct type: (*struct{})(nil)
|
|
77
|
+
if callExpr, isCallExpr := initializerExpr.(*ast.CallExpr); isCallExpr {
|
|
78
|
+
if starExpr, isStarExpr := callExpr.Fun.(*ast.StarExpr); isStarExpr {
|
|
79
|
+
if _, isStructType := starExpr.X.(*ast.StructType); isStructType {
|
|
80
|
+
// Check if the argument is nil
|
|
81
|
+
if len(callExpr.Args) == 1 {
|
|
82
|
+
if nilIdent, isIdent := callExpr.Args[0].(*ast.Ident); isIdent && nilIdent.Name == "nil" {
|
|
83
|
+
c.tsw.WriteLiterally("null")
|
|
84
|
+
return nil
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if needsBox {
|
|
93
|
+
// Boxed variable: let v: Box<T> = $.box(init_or_zero);
|
|
94
|
+
c.tsw.WriteLiterally("$.box(")
|
|
95
|
+
if hasInitializer {
|
|
96
|
+
// Write the compiled initializer expression normally
|
|
97
|
+
if err := c.WriteValueExpr(initializerExpr); err != nil {
|
|
98
|
+
return err
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
// No initializer, box the zero value
|
|
102
|
+
c.WriteZeroValueForType(goType)
|
|
103
|
+
}
|
|
104
|
+
c.tsw.WriteLiterally(")")
|
|
105
|
+
} else {
|
|
106
|
+
// Unboxed variable: let v: T = init_or_zero;
|
|
107
|
+
if hasInitializer {
|
|
108
|
+
// Handle &v initializer specifically for unboxed variables
|
|
109
|
+
if unaryExpr, isUnary := initializerExpr.(*ast.UnaryExpr); isUnary && unaryExpr.Op == token.AND {
|
|
110
|
+
// Initializer is &v
|
|
111
|
+
// Check if v is boxed
|
|
112
|
+
needsBoxOperand := false
|
|
113
|
+
unaryExprXIdent, ok := unaryExpr.X.(*ast.Ident)
|
|
114
|
+
if ok {
|
|
115
|
+
innerObj := c.pkg.TypesInfo.Uses[unaryExprXIdent]
|
|
116
|
+
needsBoxOperand = innerObj != nil && c.analysis.NeedsBoxed(innerObj)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// If v is boxed, assign the box itself (v)
|
|
120
|
+
// If v is not boxed, assign $.box(v)
|
|
121
|
+
if needsBoxOperand {
|
|
122
|
+
// special handling: do not write .value here.
|
|
123
|
+
c.WriteIdent(unaryExprXIdent, false)
|
|
124
|
+
} else {
|
|
125
|
+
// &unboxedVar -> $.box(unboxedVar)
|
|
126
|
+
c.tsw.WriteLiterally("$.box(")
|
|
127
|
+
if err := c.WriteValueExpr(unaryExpr.X); err != nil { // Write 'v'
|
|
128
|
+
return err
|
|
129
|
+
}
|
|
130
|
+
c.tsw.WriteLiterally(")")
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
// Regular initializer, clone if needed
|
|
134
|
+
if shouldApplyClone(c.pkg, initializerExpr) {
|
|
135
|
+
if err := c.WriteValueExpr(initializerExpr); err != nil {
|
|
136
|
+
return err
|
|
137
|
+
}
|
|
138
|
+
c.tsw.WriteLiterally(".clone()")
|
|
139
|
+
} else {
|
|
140
|
+
if err := c.WriteValueExpr(initializerExpr); err != nil {
|
|
141
|
+
return err
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
// No initializer, use the zero value directly
|
|
147
|
+
c.WriteZeroValueForType(goType)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
c.tsw.WriteLine("") // Finish the declaration line
|
|
151
|
+
return nil
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// --- Multi-variable declaration (existing logic seems okay, but less common for pointers) ---
|
|
155
|
+
c.tsw.WriteLiterally("let ")
|
|
156
|
+
c.tsw.WriteLiterally("[") // Use array destructuring for multi-assign
|
|
157
|
+
for i, name := range a.Names {
|
|
158
|
+
if i != 0 {
|
|
159
|
+
c.tsw.WriteLiterally(", ")
|
|
160
|
+
}
|
|
161
|
+
c.tsw.WriteLiterally(name.Name)
|
|
162
|
+
// TODO: Add type annotations for multi-var declarations if possible/needed
|
|
163
|
+
}
|
|
164
|
+
c.tsw.WriteLiterally("]")
|
|
165
|
+
if len(a.Values) > 0 {
|
|
166
|
+
// TODO: handle other kinds of assignment += -= etc.
|
|
167
|
+
c.tsw.WriteLiterally(" = ")
|
|
168
|
+
if len(a.Values) == 1 && len(a.Names) > 1 {
|
|
169
|
+
// Assign from a single multi-return value
|
|
170
|
+
if err := c.WriteValueExpr(a.Values[0]); err != nil {
|
|
171
|
+
return err
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
// Assign from multiple values
|
|
175
|
+
c.tsw.WriteLiterally("[")
|
|
176
|
+
for i, val := range a.Values {
|
|
177
|
+
if i != 0 {
|
|
178
|
+
c.tsw.WriteLiterally(", ")
|
|
179
|
+
}
|
|
180
|
+
if err := c.WriteValueExpr(val); err != nil { // Initializers are values
|
|
181
|
+
return err
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
c.tsw.WriteLiterally("]")
|
|
185
|
+
}
|
|
186
|
+
} else {
|
|
187
|
+
// No initializer, assign default values (complex for multi-assign)
|
|
188
|
+
// For simplicity, assign default array based on context (needs improvement)
|
|
189
|
+
c.tsw.WriteLiterally(" = []") // Placeholder
|
|
190
|
+
// TODO: Assign correct zero values based on types
|
|
191
|
+
}
|
|
192
|
+
c.tsw.WriteLine("") // Use WriteLine instead of WriteLine(";")
|
|
193
|
+
return nil
|
|
194
|
+
}
|