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.
@@ -0,0 +1,169 @@
1
+ package compiler
2
+
3
+ import "go/ast"
4
+
5
+ // WriteFieldList translates a Go field list (`ast.FieldList`), which can represent
6
+ // function parameters, function results, or struct fields, into its TypeScript equivalent.
7
+ // - If `isArguments` is true (for function parameters/results):
8
+ // It iterates through `a.List`, writing each field as `name: type`. Parameter
9
+ // names and types are written using `WriteField` and `WriteGoType` respectively.
10
+ // Multiple parameters are comma-separated.
11
+ // - If `isArguments` is false (for struct fields):
12
+ // It writes an opening brace `{`, indents, then writes each field definition
13
+ // using `WriteField`, followed by a closing brace `}`. If the field list is
14
+ // empty or nil, it simply writes `{}`.
15
+ //
16
+ // This function is a key part of generating TypeScript type signatures for functions
17
+ // and interfaces, as well as struct type definitions.
18
+ func (c *GoToTSCompiler) WriteFieldList(a *ast.FieldList, isArguments bool) {
19
+ if !isArguments && (a == nil || a.NumFields() == 0) {
20
+ c.tsw.WriteLiterally("{}")
21
+ return
22
+ }
23
+
24
+ if !isArguments && a.Opening.IsValid() {
25
+ c.tsw.WriteLine("{")
26
+ c.tsw.Indent(1)
27
+ }
28
+
29
+ // Check if this is a variadic function parameter list
30
+ isVariadic := false
31
+ if isArguments && a != nil && len(a.List) > 0 {
32
+ lastParam := a.List[len(a.List)-1]
33
+ if _, ok := lastParam.Type.(*ast.Ellipsis); ok {
34
+ isVariadic = true
35
+ }
36
+ }
37
+
38
+ if isArguments && isVariadic {
39
+ // Handle non-variadic parameters first
40
+ for i, field := range a.List[:len(a.List)-1] {
41
+ if i > 0 {
42
+ c.tsw.WriteLiterally(", ")
43
+ }
44
+
45
+ // Handle multiple parameter names for the same type
46
+ for j, name := range field.Names {
47
+ if j > 0 {
48
+ c.tsw.WriteLiterally(", ")
49
+ }
50
+ c.tsw.WriteLiterally(name.Name)
51
+ c.tsw.WriteLiterally(": ")
52
+ typ := c.pkg.TypesInfo.TypeOf(field.Type)
53
+ c.WriteGoType(typ, GoTypeContextGeneral)
54
+ }
55
+ }
56
+
57
+ // Handle the variadic parameter
58
+ lastParam := a.List[len(a.List)-1]
59
+ if len(a.List) > 1 {
60
+ c.tsw.WriteLiterally(", ")
61
+ }
62
+
63
+ for i, name := range lastParam.Names {
64
+ if i > 0 {
65
+ c.tsw.WriteLiterally(", ")
66
+ }
67
+ c.tsw.WriteLiterally("...")
68
+ c.tsw.WriteLiterally(name.Name)
69
+ }
70
+
71
+ c.tsw.WriteLiterally(": ")
72
+ if ellipsis, ok := lastParam.Type.(*ast.Ellipsis); ok {
73
+ c.WriteTypeExpr(ellipsis.Elt)
74
+ c.tsw.WriteLiterally("[]")
75
+ }
76
+ } else {
77
+ // Handle regular parameter list for function declarations
78
+ for i, field := range a.List {
79
+ if i > 0 && isArguments {
80
+ c.tsw.WriteLiterally(", ")
81
+ }
82
+
83
+ if isArguments {
84
+ // For function parameters with multiple names, write each with its type
85
+ for j, name := range field.Names {
86
+ if j > 0 {
87
+ c.tsw.WriteLiterally(", ")
88
+ }
89
+ c.tsw.WriteLiterally(name.Name)
90
+ c.tsw.WriteLiterally(": ")
91
+ typ := c.pkg.TypesInfo.TypeOf(field.Type)
92
+ c.WriteGoType(typ, GoTypeContextGeneral) // Use WriteGoType for parameter type
93
+ }
94
+ } else {
95
+ // For struct fields and other non-argument fields
96
+ c.WriteField(field, false)
97
+ }
98
+ }
99
+ }
100
+
101
+ if !isArguments && a.Closing.IsValid() {
102
+ c.tsw.Indent(-1)
103
+ c.tsw.WriteLine("}")
104
+ }
105
+ }
106
+
107
+ // WriteField translates a single Go field (`ast.Field`) from a field list
108
+ // (e.g., in a struct type or function signature) into its TypeScript representation.
109
+ // - If `isArguments` is false (struct field):
110
+ // - Documentation comments (`field.Doc`, `field.Comment`) are preserved.
111
+ // - If the field is anonymous (embedded), it's skipped as promotions are handled
112
+ // elsewhere (e.g., during struct class generation).
113
+ // - For named fields, it writes `public fieldName: FieldType_ts`. The field name
114
+ // retains its Go casing. The type is translated using `WriteGoType`.
115
+ // - Go struct tags (`field.Tag`) are written as a trailing comment.
116
+ //
117
+ // - If `isArguments` is true (function parameter):
118
+ // - It writes the parameter name (retaining Go casing). The type is handled
119
+ // by the caller (`WriteFieldList`).
120
+ //
121
+ // This function is used by `WriteFieldList` to process individual items within
122
+ // parameter lists and struct field definitions.
123
+ func (c *GoToTSCompiler) WriteField(field *ast.Field, isArguments bool) {
124
+ if !isArguments {
125
+ if field.Doc != nil {
126
+ c.WriteDoc(field.Doc)
127
+ }
128
+ if field.Comment != nil {
129
+ c.WriteDoc(field.Comment)
130
+ }
131
+ }
132
+
133
+ // Check if this is an embedded field (anonymous field)
134
+ if len(field.Names) == 0 && !isArguments {
135
+ // This is an embedded field, so we're adding promotions instead of declaring it directly
136
+ return
137
+ }
138
+
139
+ for i, name := range field.Names {
140
+ if i > 0 && isArguments {
141
+ c.tsw.WriteLiterally(", ")
142
+ }
143
+
144
+ // argument names: keep original casing, no access modifier
145
+ if isArguments {
146
+ c.tsw.WriteLiterally(name.Name)
147
+ // Argument type is handled in WriteFieldList, so continue
148
+ continue
149
+ } else {
150
+ // All struct fields are public in TypeScript, keeping original Go casing
151
+ c.tsw.WriteLiterally("public ")
152
+ c.tsw.WriteLiterally(name.Name)
153
+ }
154
+
155
+ // write type for struct fields (not arguments)
156
+ c.tsw.WriteLiterally(": ")
157
+ typ := c.pkg.TypesInfo.TypeOf(field.Type)
158
+ c.WriteGoType(typ, GoTypeContextGeneral) // Use WriteGoType for field type
159
+
160
+ if !isArguments {
161
+ // write tag comment if any for struct fields
162
+ if field.Tag != nil {
163
+ c.tsw.WriteCommentLinef("tag: %s", field.Tag.Value)
164
+ } else {
165
+ c.tsw.WriteLine("") // No semicolon
166
+ }
167
+ }
168
+ }
169
+ }
@@ -0,0 +1,131 @@
1
+ package compiler
2
+
3
+ import (
4
+ "fmt"
5
+ "go/ast"
6
+ "go/token"
7
+ "strconv"
8
+ )
9
+
10
+ // WriteBasicLit translates a Go basic literal (`ast.BasicLit`) into its
11
+ // TypeScript equivalent.
12
+ // - Character literals (e.g., `'a'`, `'\n'`) are translated to their numeric
13
+ // Unicode code point (e.g., `97`, `10`). Escape sequences are handled.
14
+ // - Integer, float, imaginary, and string literals are written directly as their
15
+ // `exp.Value` string, which typically corresponds to valid TypeScript syntax
16
+ // (e.g., `123`, `3.14`, `"hello"`). Imaginary literals might need special
17
+ // handling if they are to be fully supported beyond direct string output.
18
+ func (c *GoToTSCompiler) WriteBasicLit(exp *ast.BasicLit) {
19
+ if exp.Kind == token.CHAR {
20
+ // Go char literal 'x' is a rune (int32). Translate to its numeric code point.
21
+ // Use strconv.UnquoteChar to handle escape sequences correctly.
22
+ val, _, _, err := strconv.UnquoteChar(exp.Value[1:len(exp.Value)-1], '\'')
23
+ if err != nil {
24
+ c.tsw.WriteCommentInlinef("error parsing char literal %s: %v", exp.Value, err)
25
+ c.tsw.WriteLiterally("0") // Default to 0 on error
26
+ } else {
27
+ c.tsw.WriteLiterallyf("%d", val)
28
+ }
29
+ } else {
30
+ // Other literals (INT, FLOAT, STRING, IMAG)
31
+ c.tsw.WriteLiterally(exp.Value)
32
+ }
33
+ }
34
+
35
+ // WriteFuncLitValue translates a Go function literal (`ast.FuncLit`) into a
36
+ // TypeScript arrow function.
37
+ // The translation results in: `[async] (param1: type1, ...) : returnType => { ...body... }`.
38
+ // - The `async` keyword is prepended if `c.analysis.IsFuncLitAsync(exp)`
39
+ // indicates the function literal contains asynchronous operations.
40
+ // - Parameters are translated using `WriteFieldList`.
41
+ // - The return type is determined similarly to `WriteFuncType`:
42
+ // - `void` for no results.
43
+ // - `resultType` for a single unnamed result.
44
+ // - `[typeA, typeB]` for multiple or named results.
45
+ // - Wrapped in `Promise<>` if `async`.
46
+ // - The function body (`exp.Body`) is translated using `WriteStmt`.
47
+ func (c *GoToTSCompiler) WriteFuncLitValue(exp *ast.FuncLit) error {
48
+ // Determine if the function literal should be async
49
+ isAsync := c.analysis.IsFuncLitAsync(exp)
50
+
51
+ if isAsync {
52
+ c.tsw.WriteLiterally("async ")
53
+ }
54
+
55
+ // Write arrow function: (params) => { body }
56
+ c.tsw.WriteLiterally("(")
57
+
58
+ // Use WriteFieldList which now handles variadic parameters
59
+ c.WriteFieldList(exp.Type.Params, true) // true = arguments
60
+
61
+ c.tsw.WriteLiterally(")")
62
+
63
+ // Handle return type for function literals
64
+ if exp.Type.Results != nil && len(exp.Type.Results.List) > 0 {
65
+ c.tsw.WriteLiterally(": ")
66
+ if isAsync {
67
+ c.tsw.WriteLiterally("Promise<")
68
+ }
69
+ if len(exp.Type.Results.List) == 1 && len(exp.Type.Results.List[0].Names) == 0 {
70
+ c.WriteTypeExpr(exp.Type.Results.List[0].Type)
71
+ } else {
72
+ c.tsw.WriteLiterally("[")
73
+ for i, field := range exp.Type.Results.List {
74
+ if i > 0 {
75
+ c.tsw.WriteLiterally(", ")
76
+ }
77
+ c.WriteTypeExpr(field.Type)
78
+ }
79
+ c.tsw.WriteLiterally("]")
80
+ }
81
+ if isAsync {
82
+ c.tsw.WriteLiterally(">")
83
+ }
84
+ } else {
85
+ if isAsync {
86
+ c.tsw.WriteLiterally(": Promise<void>")
87
+ } else {
88
+ c.tsw.WriteLiterally(": void")
89
+ }
90
+ }
91
+
92
+ c.tsw.WriteLiterally(" => ")
93
+
94
+ hasNamedReturns := false
95
+ if exp.Type.Results != nil {
96
+ for _, field := range exp.Type.Results.List {
97
+ if len(field.Names) > 0 {
98
+ hasNamedReturns = true
99
+ break
100
+ }
101
+ }
102
+ }
103
+
104
+ if hasNamedReturns {
105
+ c.tsw.WriteLine("{")
106
+ c.tsw.Indent(1)
107
+
108
+ // Declare named return variables and initialize them to their zero values
109
+ for _, field := range exp.Type.Results.List {
110
+ for _, name := range field.Names {
111
+ c.tsw.WriteLiterallyf("let %s: ", name.Name)
112
+ c.WriteTypeExpr(field.Type)
113
+ c.tsw.WriteLiterally(" = ")
114
+ c.WriteZeroValueForType(c.pkg.TypesInfo.TypeOf(field.Type))
115
+ c.tsw.WriteLine("")
116
+ }
117
+ }
118
+ }
119
+
120
+ // Write function body
121
+ if err := c.WriteStmtBlock(exp.Body, false); err != nil {
122
+ return fmt.Errorf("failed to write block statement: %w", err)
123
+ }
124
+
125
+ if hasNamedReturns {
126
+ c.tsw.Indent(-1)
127
+ c.tsw.WriteLiterally("}")
128
+ }
129
+
130
+ return nil
131
+ }
@@ -0,0 +1,148 @@
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
+
33
+ // TODO: add bigint support
34
+ // "int64": "bigint", // Requires TypeScript target >= ES2020
35
+ "int64": "number",
36
+
37
+ // Unsigned Integers
38
+ "uint": "number",
39
+ "uint8": "number", // byte is an alias for uint8
40
+ "byte": "number",
41
+ "uint16": "number",
42
+ "uint32": "number",
43
+
44
+ // TODO: add bigint support
45
+ // "uint64": "bigint", // Requires TypeScript target >= ES2020
46
+ "uint64": "number",
47
+
48
+ // Floating Point Numbers
49
+ "float32": "number",
50
+ "float64": "number",
51
+ }
52
+
53
+ func isPrimitiveType(name string) bool {
54
+ _, ok := goToTypescriptPrimitives[name]
55
+ return ok
56
+ }
57
+
58
+ // GoBuiltinToTypescript translates a Go built-in primitive type name (string)
59
+ // to its TypeScript equivalent. It uses the `goToTypescriptPrimitives` map
60
+ // for the conversion.
61
+ // It returns the TypeScript type name and `true` if the Go type name is found
62
+ // in the map. Otherwise, it returns an empty string and `false`.
63
+ // This function only handles primitive types listed in the map; composite types
64
+ // or custom types are not processed here.
65
+ func GoBuiltinToTypescript(typeName string) (string, bool) {
66
+ val, ok := goToTypescriptPrimitives[typeName]
67
+ return val, ok
68
+ }
69
+
70
+ // tokenMap provides a mapping from Go `token.Token` types (representing operators
71
+ // and punctuation) to their corresponding string representations in TypeScript.
72
+ // This map is used by `TokenToTs` to translate Go operators during expression
73
+ // and statement compilation.
74
+ //
75
+ // Examples:
76
+ // - `token.ADD` (Go `+`) -> `"+"` (TypeScript `+`)
77
+ // - `token.LAND` (Go `&&`) -> `"&&"` (TypeScript `&&`)
78
+ // - `token.ASSIGN` (Go `=`) -> `"="` (TypeScript `=`)
79
+ // - `token.DEFINE` (Go `:=`) -> `"="` (TypeScript `=`, as `let` is handled separately)
80
+ //
81
+ // Some tokens like `token.ARROW` (channel send/receive) are handled specially
82
+ // in their respective expression/statement writers and might not be directly mapped here.
83
+ // Bitwise AND NOT (`&^=`) is also mapped but may require specific runtime support if not directly translatable.
84
+ var tokenMap = map[token.Token]string{
85
+ token.ADD: "+",
86
+ token.SUB: "-",
87
+ token.MUL: "*",
88
+ token.QUO: "/",
89
+ token.REM: "%",
90
+ token.AND: "&",
91
+ token.OR: "|",
92
+ token.XOR: "^",
93
+ token.SHL: "<<",
94
+ token.SHR: ">>",
95
+
96
+ token.ADD_ASSIGN: "+=",
97
+ token.SUB_ASSIGN: "-=",
98
+ token.MUL_ASSIGN: "*=",
99
+ token.QUO_ASSIGN: "/=",
100
+ token.REM_ASSIGN: "%=",
101
+
102
+ token.AND_ASSIGN: "&=",
103
+ token.OR_ASSIGN: "|=",
104
+ token.XOR_ASSIGN: "^=", // TODO: check if this works
105
+ token.SHL_ASSIGN: "<<=",
106
+ token.SHR_ASSIGN: ">>=",
107
+ token.AND_NOT_ASSIGN: "&^=",
108
+
109
+ token.LAND: "&&",
110
+ token.LOR: "||",
111
+ // token.ARROW: ""
112
+ token.INC: "++",
113
+ token.DEC: "--",
114
+ token.EQL: "==",
115
+ token.LSS: "<",
116
+ token.GTR: ">",
117
+ token.ASSIGN: "=",
118
+ token.NOT: "!",
119
+
120
+ token.NEQ: "!=",
121
+ token.LEQ: "<=",
122
+ token.GEQ: ">=",
123
+ token.DEFINE: "=", // :=
124
+ token.ELLIPSIS: "...", // TODO
125
+
126
+ token.LPAREN: "(",
127
+ token.LBRACK: "[",
128
+ token.LBRACE: "{",
129
+ token.COMMA: ",",
130
+ token.PERIOD: ".",
131
+
132
+ token.RPAREN: ")",
133
+ token.RBRACK: "]",
134
+ token.RBRACE: "}",
135
+ token.SEMICOLON: ";",
136
+ token.COLON: ":",
137
+ }
138
+
139
+ // TokenToTs converts a Go `token.Token` (representing an operator or punctuation)
140
+ // into its corresponding TypeScript string representation using the `tokenMap`.
141
+ // It returns the TypeScript string and `true` if the token is found in the map.
142
+ // Otherwise, it returns an empty string and `false`. This function is essential
143
+ // for translating expressions involving operators (e.g., arithmetic, logical,
144
+ // assignment operators).
145
+ func TokenToTs(tok token.Token) (string, bool) {
146
+ t, ok := tokenMap[tok]
147
+ return t, ok
148
+ }