goscript 0.0.7 → 0.0.10
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/README.md +13 -5
- package/builtin/builtin.ts +180 -0
- package/compiler/compile_expr.go +119 -25
- package/compiler/compile_spec.go +175 -2
- package/compiler/compile_stmt.go +99 -69
- package/compiler/writer.go +2 -2
- package/dist/builtin/builtin.d.ts +55 -0
- package/dist/builtin/builtin.js +126 -0
- package/dist/builtin/builtin.js.map +1 -1
- package/dist/compliance/tests/array_literal/array_literal.gs.js +1 -0
- package/dist/compliance/tests/array_literal/array_literal.gs.js.map +1 -1
- package/dist/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.js +4 -0
- package/dist/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.js.map +1 -1
- package/dist/compliance/tests/copy_independence/copy_independence.gs.js +3 -1
- package/dist/compliance/tests/copy_independence/copy_independence.gs.js.map +1 -1
- package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.d.ts +1 -0
- package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.js +37 -0
- package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.js.map +1 -0
- package/dist/compliance/tests/float64/float64.gs.js +1 -0
- package/dist/compliance/tests/float64/float64.gs.js.map +1 -1
- package/dist/compliance/tests/for_loop_condition_only/for_loop_condition_only.gs.d.ts +1 -0
- package/dist/compliance/tests/for_loop_condition_only/for_loop_condition_only.gs.js +11 -0
- package/dist/compliance/tests/for_loop_condition_only/for_loop_condition_only.gs.js.map +1 -0
- package/dist/compliance/tests/func_literal/func_literal.gs.js.map +1 -1
- package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.d.ts +2 -0
- package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.js +4 -0
- package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.js.map +1 -1
- package/dist/compliance/tests/interface_multi_param_return/interface_multi_param_return.gs.d.ts +1 -0
- package/dist/compliance/tests/interface_multi_param_return/interface_multi_param_return.gs.js +34 -0
- package/dist/compliance/tests/interface_multi_param_return/interface_multi_param_return.gs.js.map +1 -0
- package/dist/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.js +8 -2
- package/dist/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.js.map +1 -1
- package/dist/compliance/tests/interface_type_assertion/interface_type_assertion.gs.js +6 -2
- package/dist/compliance/tests/interface_type_assertion/interface_type_assertion.gs.js.map +1 -1
- package/dist/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.js +3 -1
- package/dist/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.js.map +1 -1
- package/dist/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.js +3 -0
- package/dist/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.js.map +1 -1
- package/dist/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.js +3 -0
- package/dist/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.js.map +1 -1
- package/dist/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.js +3 -0
- package/dist/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.js.map +1 -1
- package/dist/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.js +3 -0
- package/dist/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.js.map +1 -1
- package/dist/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.js +3 -0
- package/dist/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.js.map +1 -1
- package/dist/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.js +3 -0
- package/dist/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.js.map +1 -1
- package/dist/compliance/tests/pointer_initialization/pointer_initialization.gs.js +3 -1
- package/dist/compliance/tests/pointer_initialization/pointer_initialization.gs.js.map +1 -1
- package/dist/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.js +17 -16
- package/dist/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.js.map +1 -1
- package/dist/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.gs.js +13 -13
- package/dist/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.gs.js.map +1 -1
- package/dist/compliance/tests/select_statement/select_statement.gs.js +119 -119
- package/dist/compliance/tests/select_statement/select_statement.gs.js.map +1 -1
- package/dist/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.js +3 -1
- package/dist/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.js.map +1 -1
- package/dist/compliance/tests/string_rune_conversion/string_rune_conversion.gs.js.map +1 -1
- package/dist/compliance/tests/struct_field_access/struct_field_access.gs.js +3 -0
- package/dist/compliance/tests/struct_field_access/struct_field_access.gs.js.map +1 -1
- package/dist/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.js +3 -0
- package/dist/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.js.map +1 -1
- package/dist/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.js +3 -1
- package/dist/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -463,11 +463,19 @@ Code is compiled with `GOARCH=js` and uses a 32-bit environment similar to wasm.
|
|
|
463
463
|
All Go import paths are prefixed with `@go/` and can be imported in TypeScript:
|
|
464
464
|
|
|
465
465
|
```typescript
|
|
466
|
-
import {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
466
|
+
import { MyAsyncFunction, MyStruct } from '@go/github.com/myorg/mypackage';
|
|
467
|
+
|
|
468
|
+
// Example of importing and using a compiled Go async function
|
|
469
|
+
async function runGoCode() {
|
|
470
|
+
// Call an async function compiled from Go
|
|
471
|
+
const result = await MyAsyncFunction("input data");
|
|
472
|
+
console.log("Result from Go async function:", result);
|
|
473
|
+
|
|
474
|
+
// You can still use synchronous types and functions
|
|
475
|
+
let myThing = new MyStruct();
|
|
476
|
+
myThing.GetMyString();
|
|
477
|
+
runGoCode();
|
|
478
|
+
}
|
|
471
479
|
```
|
|
472
480
|
|
|
473
481
|
## License
|
package/builtin/builtin.ts
CHANGED
|
@@ -46,6 +46,13 @@ export const cap = <T>(slice: Array<T> & { __capacity?: number }): number => {
|
|
|
46
46
|
return slice.__capacity !== undefined ? slice.__capacity : slice.length
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Represents the Go error type (interface).
|
|
51
|
+
*/
|
|
52
|
+
export type Error = {
|
|
53
|
+
Error(): string;
|
|
54
|
+
} | null;
|
|
55
|
+
|
|
49
56
|
/**
|
|
50
57
|
* Converts a string to an array of Unicode code points (runes).
|
|
51
58
|
* @param str The input string.
|
|
@@ -111,6 +118,179 @@ export const append = <T>(slice: Array<T>, ...elements: T[]): Array<T> => {
|
|
|
111
118
|
return slice
|
|
112
119
|
}
|
|
113
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Represents the kinds of Go types that can be registered at runtime.
|
|
123
|
+
*/
|
|
124
|
+
export enum TypeKind {
|
|
125
|
+
Struct = "struct",
|
|
126
|
+
Interface = "interface",
|
|
127
|
+
Basic = "basic",
|
|
128
|
+
Pointer = "pointer",
|
|
129
|
+
Slice = "slice",
|
|
130
|
+
Map = "map",
|
|
131
|
+
Channel = "channel",
|
|
132
|
+
Function = "function",
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Represents type information for a Go type in the runtime.
|
|
137
|
+
*/
|
|
138
|
+
export interface TypeInfo {
|
|
139
|
+
name: string;
|
|
140
|
+
kind: TypeKind;
|
|
141
|
+
zeroValue: any;
|
|
142
|
+
// For interfaces, the set of methods
|
|
143
|
+
methods?: Set<string>;
|
|
144
|
+
// For structs, the constructor
|
|
145
|
+
constructor?: new (...args: any[]) => any;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Registry to store runtime type information
|
|
149
|
+
const typeRegistry = new Map<string, TypeInfo>();
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Registers a type with the runtime type system.
|
|
153
|
+
*
|
|
154
|
+
* @param name The name of the type.
|
|
155
|
+
* @param kind The kind of the type.
|
|
156
|
+
* @param zeroValue The zero value for the type.
|
|
157
|
+
* @param methods Optional set of method names for interfaces.
|
|
158
|
+
* @param constructor Optional constructor for structs.
|
|
159
|
+
* @returns The type information object for chaining.
|
|
160
|
+
*/
|
|
161
|
+
export const registerType = (
|
|
162
|
+
name: string,
|
|
163
|
+
kind: TypeKind,
|
|
164
|
+
zeroValue: any,
|
|
165
|
+
methods?: Set<string>,
|
|
166
|
+
constructor?: new (...args: any[]) => any
|
|
167
|
+
): TypeInfo => {
|
|
168
|
+
const typeInfo: TypeInfo = {
|
|
169
|
+
name,
|
|
170
|
+
kind,
|
|
171
|
+
zeroValue,
|
|
172
|
+
methods,
|
|
173
|
+
constructor,
|
|
174
|
+
};
|
|
175
|
+
typeRegistry.set(name, typeInfo);
|
|
176
|
+
return typeInfo;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Represents the result of a type assertion.
|
|
181
|
+
*/
|
|
182
|
+
export interface TypeAssertResult<T> {
|
|
183
|
+
value: T;
|
|
184
|
+
ok: boolean;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Performs a type assertion at runtime.
|
|
189
|
+
*
|
|
190
|
+
* @param value The value to assert.
|
|
191
|
+
* @param typeName The name of the target type.
|
|
192
|
+
* @returns An object with the asserted value and whether the assertion succeeded.
|
|
193
|
+
*/
|
|
194
|
+
export function typeAssert<T>(value: any, typeName: string): TypeAssertResult<T> {
|
|
195
|
+
// Get the type information from the registry
|
|
196
|
+
const typeInfo = typeRegistry.get(typeName);
|
|
197
|
+
if (!typeInfo) {
|
|
198
|
+
console.warn(`Type information for '${typeName}' not found in registry.`);
|
|
199
|
+
return { value: null as unknown as T, ok: false };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// If value is null or undefined, assertion fails
|
|
203
|
+
if (value === null || value === undefined) {
|
|
204
|
+
return { value: typeInfo.zeroValue as T, ok: false };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Check based on the kind of the target type
|
|
208
|
+
switch (typeInfo.kind) {
|
|
209
|
+
case TypeKind.Struct:
|
|
210
|
+
// For structs, use instanceof with the constructor
|
|
211
|
+
if (typeInfo.constructor && value instanceof typeInfo.constructor) {
|
|
212
|
+
return { value: value as T, ok: true };
|
|
213
|
+
}
|
|
214
|
+
break;
|
|
215
|
+
|
|
216
|
+
case TypeKind.Interface:
|
|
217
|
+
// For interfaces, check if the value has all the required methods
|
|
218
|
+
if (typeInfo.methods && typeof value === 'object') {
|
|
219
|
+
const allMethodsPresent = Array.from(typeInfo.methods).every(
|
|
220
|
+
(method) => typeof (value as any)[method] === 'function'
|
|
221
|
+
);
|
|
222
|
+
if (allMethodsPresent) {
|
|
223
|
+
return { value: value as T, ok: true };
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
break;
|
|
227
|
+
|
|
228
|
+
case TypeKind.Basic:
|
|
229
|
+
// For basic types, check if the value matches the expected JavaScript type
|
|
230
|
+
// This is a simple check for common basic types
|
|
231
|
+
const basicType = typeof value;
|
|
232
|
+
if (
|
|
233
|
+
basicType === 'string' ||
|
|
234
|
+
basicType === 'number' ||
|
|
235
|
+
basicType === 'boolean'
|
|
236
|
+
) {
|
|
237
|
+
return { value: value as T, ok: true };
|
|
238
|
+
}
|
|
239
|
+
break;
|
|
240
|
+
|
|
241
|
+
case TypeKind.Pointer:
|
|
242
|
+
// For pointers, check if value is not null or undefined
|
|
243
|
+
// In Go, pointers can be nil which we represent as null/undefined in TS
|
|
244
|
+
if (value !== null && value !== undefined) {
|
|
245
|
+
return { value: value as T, ok: true };
|
|
246
|
+
}
|
|
247
|
+
break;
|
|
248
|
+
|
|
249
|
+
case TypeKind.Slice:
|
|
250
|
+
// For slices, check if the value is an array
|
|
251
|
+
if (Array.isArray(value)) {
|
|
252
|
+
return { value: value as T, ok: true };
|
|
253
|
+
}
|
|
254
|
+
break;
|
|
255
|
+
|
|
256
|
+
case TypeKind.Map:
|
|
257
|
+
// For maps, check if the value is a Map
|
|
258
|
+
if (value instanceof Map) {
|
|
259
|
+
return { value: value as T, ok: true };
|
|
260
|
+
}
|
|
261
|
+
break;
|
|
262
|
+
|
|
263
|
+
case TypeKind.Channel:
|
|
264
|
+
// For channels, check if the value has the required Channel interface methods
|
|
265
|
+
if (
|
|
266
|
+
typeof value === 'object' &&
|
|
267
|
+
value !== null &&
|
|
268
|
+
'send' in value &&
|
|
269
|
+
'receive' in value &&
|
|
270
|
+
'close' in value &&
|
|
271
|
+
typeof value.send === 'function' &&
|
|
272
|
+
typeof value.receive === 'function' &&
|
|
273
|
+
typeof value.close === 'function'
|
|
274
|
+
) {
|
|
275
|
+
return { value: value as T, ok: true };
|
|
276
|
+
}
|
|
277
|
+
break;
|
|
278
|
+
|
|
279
|
+
case TypeKind.Function:
|
|
280
|
+
// For functions, check if the value is a function
|
|
281
|
+
if (typeof value === 'function') {
|
|
282
|
+
return { value: value as T, ok: true };
|
|
283
|
+
}
|
|
284
|
+
break;
|
|
285
|
+
|
|
286
|
+
default:
|
|
287
|
+
console.warn(`Type assertion for kind '${typeInfo.kind}' not implemented.`);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Assertion failed
|
|
291
|
+
return { value: typeInfo.zeroValue as T, ok: false };
|
|
292
|
+
}
|
|
293
|
+
|
|
114
294
|
/**
|
|
115
295
|
* Represents the result of a channel receive operation with 'ok' value
|
|
116
296
|
*/
|
package/compiler/compile_expr.go
CHANGED
|
@@ -7,6 +7,7 @@ import (
|
|
|
7
7
|
"go/token"
|
|
8
8
|
gtypes "go/types"
|
|
9
9
|
"strconv"
|
|
10
|
+
"strings"
|
|
10
11
|
|
|
11
12
|
gstypes "github.com/paralin/goscript/compiler/types"
|
|
12
13
|
)
|
|
@@ -72,6 +73,9 @@ func (c *GoToTSCompiler) WriteValueExpr(a ast.Expr) error {
|
|
|
72
73
|
return c.WriteCompositeLitValue(exp)
|
|
73
74
|
case *ast.KeyValueExpr:
|
|
74
75
|
return c.WriteKeyValueExprValue(exp)
|
|
76
|
+
case *ast.TypeAssertExpr:
|
|
77
|
+
// Handle type assertion in an expression context
|
|
78
|
+
return c.WriteTypeAssertExpr(exp)
|
|
75
79
|
case *ast.IndexExpr:
|
|
76
80
|
// Handle map access: use Map.get() instead of brackets for reading values
|
|
77
81
|
if tv, ok := c.pkg.TypesInfo.Types[exp.X]; ok {
|
|
@@ -121,11 +125,51 @@ func (c *GoToTSCompiler) WriteValueExpr(a ast.Expr) error {
|
|
|
121
125
|
}
|
|
122
126
|
}
|
|
123
127
|
|
|
128
|
+
// WriteTypeAssertExpr writes a type assertion expression.
|
|
129
|
+
func (c *GoToTSCompiler) WriteTypeAssertExpr(exp *ast.TypeAssertExpr) error {
|
|
130
|
+
// Get the type name string for the asserted type
|
|
131
|
+
typeName := c.getTypeNameString(exp.Type)
|
|
132
|
+
|
|
133
|
+
// Generate a call to goscript.typeAssert
|
|
134
|
+
c.tsw.WriteLiterally("goscript.typeAssert<")
|
|
135
|
+
c.WriteTypeExpr(exp.Type) // Write the asserted type for the generic
|
|
136
|
+
c.tsw.WriteLiterally(">(")
|
|
137
|
+
if err := c.WriteValueExpr(exp.X); err != nil { // The interface expression
|
|
138
|
+
return fmt.Errorf("failed to write interface expression in type assertion expression: %w", err)
|
|
139
|
+
}
|
|
140
|
+
c.tsw.WriteLiterally(", ")
|
|
141
|
+
c.tsw.WriteLiterally(fmt.Sprintf("'%s'", typeName))
|
|
142
|
+
c.tsw.WriteLiterally(").value") // Access the value field directly in expression context
|
|
143
|
+
return nil
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// getTypeNameString returns the string representation of a type name
|
|
147
|
+
func (c *GoToTSCompiler) getTypeNameString(typeExpr ast.Expr) string {
|
|
148
|
+
switch t := typeExpr.(type) {
|
|
149
|
+
case *ast.Ident:
|
|
150
|
+
return t.Name
|
|
151
|
+
case *ast.SelectorExpr:
|
|
152
|
+
// For imported types like pkg.Type
|
|
153
|
+
if ident, ok := t.X.(*ast.Ident); ok {
|
|
154
|
+
return fmt.Sprintf("%s.%s", ident.Name, t.Sel.Name)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// Default case, use a placeholder for complex types
|
|
158
|
+
return "unknown"
|
|
159
|
+
}
|
|
160
|
+
|
|
124
161
|
// --- Exported Node-Specific Writers ---
|
|
125
162
|
|
|
126
163
|
// WriteIdentType writes an identifier used as a type.
|
|
127
164
|
func (c *GoToTSCompiler) WriteIdentType(exp *ast.Ident) {
|
|
128
165
|
name := exp.Name
|
|
166
|
+
|
|
167
|
+
// Special case for the built-in error interface
|
|
168
|
+
if name == "error" {
|
|
169
|
+
c.tsw.WriteLiterally("goscript.Error")
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
|
|
129
173
|
if tsname, ok := gstypes.GoBuiltinToTypescript(name); ok {
|
|
130
174
|
name = tsname
|
|
131
175
|
} else {
|
|
@@ -150,9 +194,13 @@ func (c *GoToTSCompiler) WriteIdentType(exp *ast.Ident) {
|
|
|
150
194
|
c.tsw.WriteLiterally(name)
|
|
151
195
|
}
|
|
152
196
|
|
|
153
|
-
// WriteIdentValue writes an identifier used as a value (variable, function name).
|
|
197
|
+
// WriteIdentValue writes an identifier used as a value (variable, function name, nil).
|
|
154
198
|
func (c *GoToTSCompiler) WriteIdentValue(exp *ast.Ident) {
|
|
155
|
-
|
|
199
|
+
if exp.Name == "nil" {
|
|
200
|
+
c.tsw.WriteLiterally("null")
|
|
201
|
+
} else {
|
|
202
|
+
c.tsw.WriteLiterally(exp.Name)
|
|
203
|
+
}
|
|
156
204
|
}
|
|
157
205
|
|
|
158
206
|
// WriteSelectorExprType writes a selector expression used as a type (e.g., pkg.Type).
|
|
@@ -205,32 +253,47 @@ func (c *GoToTSCompiler) WriteStructType(exp *ast.StructType) {
|
|
|
205
253
|
|
|
206
254
|
// WriteInterfaceType writes an interface type definition.
|
|
207
255
|
func (c *GoToTSCompiler) WriteInterfaceType(exp *ast.InterfaceType) {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
// Keep original Go casing for method names
|
|
217
|
-
methodName := method.Names[0]
|
|
218
|
-
c.WriteIdentValue(methodName)
|
|
219
|
-
|
|
220
|
-
// Method signature is a FuncType
|
|
221
|
-
if funcType, ok := method.Type.(*ast.FuncType); ok {
|
|
222
|
-
c.WriteFuncType(funcType, false) // Interface method signatures are not async
|
|
256
|
+
var embeddedInterfaces []string
|
|
257
|
+
var methods []*ast.Field
|
|
258
|
+
|
|
259
|
+
if exp.Methods != nil {
|
|
260
|
+
for _, method := range exp.Methods.List {
|
|
261
|
+
if len(method.Names) > 0 {
|
|
262
|
+
// Named method
|
|
263
|
+
methods = append(methods, method)
|
|
223
264
|
} else {
|
|
224
|
-
//
|
|
225
|
-
c.
|
|
265
|
+
// Embedded interface - collect the type name using type info
|
|
266
|
+
if tv, ok := c.pkg.TypesInfo.Types[method.Type]; ok && tv.Type != nil {
|
|
267
|
+
if namedType, ok := tv.Type.(*gtypes.Named); ok {
|
|
268
|
+
embeddedInterfaces = append(embeddedInterfaces, namedType.Obj().Name())
|
|
269
|
+
} else {
|
|
270
|
+
c.tsw.WriteCommentLine(fmt.Sprintf("// Unhandled embedded interface type: %T", tv.Type))
|
|
271
|
+
}
|
|
272
|
+
} else {
|
|
273
|
+
c.tsw.WriteCommentLine("// Could not resolve embedded interface type")
|
|
274
|
+
}
|
|
226
275
|
}
|
|
227
|
-
c.tsw.WriteLine(";")
|
|
228
|
-
} else {
|
|
229
|
-
// Embedded interface - write the type name
|
|
230
|
-
c.WriteTypeExpr(method.Type)
|
|
231
|
-
c.tsw.WriteLine("; // Embedded interface - requires manual merging or mixin in TS")
|
|
232
276
|
}
|
|
233
277
|
}
|
|
278
|
+
|
|
279
|
+
// If there are embedded interfaces, write the extends clause
|
|
280
|
+
if len(embeddedInterfaces) > 0 {
|
|
281
|
+
c.tsw.WriteLiterally(" extends ")
|
|
282
|
+
c.tsw.WriteLiterally(strings.Join(embeddedInterfaces, ", "))
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Write the interface body on the same line
|
|
286
|
+
c.tsw.WriteLiterally("{") // Removed leading space
|
|
287
|
+
c.tsw.WriteLine("") // Newline after opening brace
|
|
288
|
+
c.tsw.Indent(1)
|
|
289
|
+
|
|
290
|
+
// Write named methods
|
|
291
|
+
for _, method := range methods {
|
|
292
|
+
if err := c.WriteInterfaceMethodSignature(method); err != nil {
|
|
293
|
+
c.tsw.WriteCommentInline(fmt.Sprintf("error writing interface method signature: %v", err))
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
234
297
|
c.tsw.Indent(-1)
|
|
235
298
|
c.tsw.WriteLine("}")
|
|
236
299
|
}
|
|
@@ -790,7 +853,38 @@ func (c *GoToTSCompiler) WriteFuncLitValue(exp *ast.FuncLit) error {
|
|
|
790
853
|
// Write arrow function: (params) => { body }
|
|
791
854
|
c.tsw.WriteLiterally("(")
|
|
792
855
|
c.WriteFieldList(exp.Type.Params, true) // true = arguments
|
|
793
|
-
c.tsw.WriteLiterally(")
|
|
856
|
+
c.tsw.WriteLiterally(")")
|
|
857
|
+
|
|
858
|
+
// Handle return type for function literals
|
|
859
|
+
if exp.Type.Results != nil && len(exp.Type.Results.List) > 0 {
|
|
860
|
+
c.tsw.WriteLiterally(": ")
|
|
861
|
+
if isAsync {
|
|
862
|
+
c.tsw.WriteLiterally("Promise<")
|
|
863
|
+
}
|
|
864
|
+
if len(exp.Type.Results.List) == 1 && len(exp.Type.Results.List[0].Names) == 0 {
|
|
865
|
+
c.WriteTypeExpr(exp.Type.Results.List[0].Type)
|
|
866
|
+
} else {
|
|
867
|
+
c.tsw.WriteLiterally("[")
|
|
868
|
+
for i, field := range exp.Type.Results.List {
|
|
869
|
+
if i > 0 {
|
|
870
|
+
c.tsw.WriteLiterally(", ")
|
|
871
|
+
}
|
|
872
|
+
c.WriteTypeExpr(field.Type)
|
|
873
|
+
}
|
|
874
|
+
c.tsw.WriteLiterally("]")
|
|
875
|
+
}
|
|
876
|
+
if isAsync {
|
|
877
|
+
c.tsw.WriteLiterally(">")
|
|
878
|
+
}
|
|
879
|
+
} else {
|
|
880
|
+
if isAsync {
|
|
881
|
+
c.tsw.WriteLiterally(": Promise<void>")
|
|
882
|
+
} else {
|
|
883
|
+
c.tsw.WriteLiterally(": void")
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
c.tsw.WriteLiterally(" => ")
|
|
794
888
|
|
|
795
889
|
// Write function body
|
|
796
890
|
if err := c.WriteStmt(exp.Body); err != nil {
|
package/compiler/compile_spec.go
CHANGED
|
@@ -3,6 +3,9 @@ package compiler
|
|
|
3
3
|
import (
|
|
4
4
|
"fmt"
|
|
5
5
|
"go/ast"
|
|
6
|
+
"go/types"
|
|
7
|
+
"sort"
|
|
8
|
+
"strings"
|
|
6
9
|
)
|
|
7
10
|
|
|
8
11
|
// WriteSpec writes a specification to the output.
|
|
@@ -24,6 +27,88 @@ func (c *GoToTSCompiler) WriteSpec(a ast.Spec) error {
|
|
|
24
27
|
return nil
|
|
25
28
|
}
|
|
26
29
|
|
|
30
|
+
// collectMethodNames returns a comma-separated string of method names for a struct
|
|
31
|
+
func (c *GoToTSCompiler) collectMethodNames(structName string) string {
|
|
32
|
+
var methodNames []string
|
|
33
|
+
|
|
34
|
+
for _, fileSyntax := range c.pkg.Syntax {
|
|
35
|
+
for _, decl := range fileSyntax.Decls {
|
|
36
|
+
funcDecl, isFunc := decl.(*ast.FuncDecl)
|
|
37
|
+
if !isFunc || funcDecl.Recv == nil || len(funcDecl.Recv.List) == 0 {
|
|
38
|
+
continue // Skip non-functions or functions without receivers
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Check if the receiver type matches the struct name
|
|
42
|
+
recvField := funcDecl.Recv.List[0]
|
|
43
|
+
recvType := recvField.Type
|
|
44
|
+
// Handle pointer receivers (*MyStruct) and value receivers (MyStruct)
|
|
45
|
+
if starExpr, ok := recvType.(*ast.StarExpr); ok {
|
|
46
|
+
recvType = starExpr.X // Get the type being pointed to
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Check if the receiver identifier name matches the struct name
|
|
50
|
+
if ident, ok := recvType.(*ast.Ident); ok && ident.Name == structName {
|
|
51
|
+
// Found a method for this struct
|
|
52
|
+
methodNames = append(methodNames, fmt.Sprintf("'%s'", funcDecl.Name.Name))
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return strings.Join(methodNames, ", ")
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// collectInterfaceMethods returns a comma-separated string of method names for an interface
|
|
61
|
+
func (c *GoToTSCompiler) collectInterfaceMethods(interfaceType *ast.InterfaceType) string {
|
|
62
|
+
// Use a map to ensure uniqueness of method names
|
|
63
|
+
methodNamesMap := make(map[string]struct{})
|
|
64
|
+
|
|
65
|
+
if interfaceType.Methods != nil {
|
|
66
|
+
for _, method := range interfaceType.Methods.List {
|
|
67
|
+
if len(method.Names) > 0 {
|
|
68
|
+
// Named method
|
|
69
|
+
methodNamesMap[method.Names[0].Name] = struct{}{}
|
|
70
|
+
} else {
|
|
71
|
+
// Embedded interface - resolve it and collect its methods
|
|
72
|
+
embeddedType := method.Type
|
|
73
|
+
|
|
74
|
+
// Resolve the embedded interface using type information
|
|
75
|
+
if tv, ok := c.pkg.TypesInfo.Types[embeddedType]; ok && tv.Type != nil {
|
|
76
|
+
// Get the underlying interface type
|
|
77
|
+
var ifaceType *types.Interface
|
|
78
|
+
if named, ok := tv.Type.(*types.Named); ok {
|
|
79
|
+
if underlying, ok := named.Underlying().(*types.Interface); ok {
|
|
80
|
+
ifaceType = underlying
|
|
81
|
+
}
|
|
82
|
+
} else if underlying, ok := tv.Type.(*types.Interface); ok {
|
|
83
|
+
ifaceType = underlying
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Collect methods from the interface
|
|
87
|
+
if ifaceType != nil {
|
|
88
|
+
for i := 0; i < ifaceType.NumMethods(); i++ {
|
|
89
|
+
methodNamesMap[ifaceType.Method(i).Name()] = struct{}{}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
// Couldn't resolve the embedded interface
|
|
94
|
+
c.tsw.WriteCommentLine("// Note: Some embedded interface methods may not be fully resolved")
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Convert the map to a slice for a deterministic output order
|
|
101
|
+
var methodNames []string
|
|
102
|
+
for name := range methodNamesMap {
|
|
103
|
+
methodNames = append(methodNames, fmt.Sprintf("'%s'", name))
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Sort for deterministic output
|
|
107
|
+
sort.Strings(methodNames)
|
|
108
|
+
|
|
109
|
+
return strings.Join(methodNames, ", ")
|
|
110
|
+
}
|
|
111
|
+
|
|
27
112
|
// WriteTypeSpec writes the type specification to the output.
|
|
28
113
|
func (c *GoToTSCompiler) WriteTypeSpec(a *ast.TypeSpec) error {
|
|
29
114
|
if a.Doc != nil {
|
|
@@ -81,6 +166,18 @@ func (c *GoToTSCompiler) WriteTypeSpec(a *ast.TypeSpec) error {
|
|
|
81
166
|
c.tsw.WriteLine("")
|
|
82
167
|
c.tsw.WriteLinef("constructor(init?: Partial<%s>) { if (init) Object.assign(this, init as any); }", className)
|
|
83
168
|
c.tsw.WriteLinef("public clone(): %s { return Object.assign(Object.create(%s.prototype) as %s, this); }", className, className, className)
|
|
169
|
+
|
|
170
|
+
// Add code to register the type with the runtime system
|
|
171
|
+
c.tsw.WriteLine("")
|
|
172
|
+
c.tsw.WriteLinef("// Register this type with the runtime type system")
|
|
173
|
+
c.tsw.WriteLinef("static __typeInfo = goscript.registerType(")
|
|
174
|
+
c.tsw.WriteLinef(" '%s',", className)
|
|
175
|
+
c.tsw.WriteLinef(" goscript.TypeKind.Struct,")
|
|
176
|
+
c.tsw.WriteLinef(" new %s(),", className)
|
|
177
|
+
c.tsw.WriteLinef(" new Set([%s]),", c.collectMethodNames(className))
|
|
178
|
+
c.tsw.WriteLinef(" %s", className)
|
|
179
|
+
c.tsw.WriteLinef(");")
|
|
180
|
+
|
|
84
181
|
c.tsw.Indent(-1)
|
|
85
182
|
c.tsw.WriteLine("}")
|
|
86
183
|
case *ast.InterfaceType:
|
|
@@ -88,8 +185,20 @@ func (c *GoToTSCompiler) WriteTypeSpec(a *ast.TypeSpec) error {
|
|
|
88
185
|
if err := c.WriteValueExpr(a.Name); err != nil { // Interface name is a value identifier
|
|
89
186
|
return err
|
|
90
187
|
}
|
|
91
|
-
c.tsw.
|
|
92
|
-
c.WriteTypeExpr(a.Type)
|
|
188
|
+
c.tsw.WriteLiterally(" ") // Changed from WriteLine to WriteLiterally
|
|
189
|
+
c.WriteTypeExpr(a.Type) // The interface definition itself is a type
|
|
190
|
+
|
|
191
|
+
// Add code to register the interface with the runtime system
|
|
192
|
+
interfaceName := a.Name.Name
|
|
193
|
+
c.tsw.WriteLine("")
|
|
194
|
+
c.tsw.WriteLinef("// Register this interface with the runtime type system")
|
|
195
|
+
c.tsw.WriteLinef("const %s__typeInfo = goscript.registerType(", interfaceName)
|
|
196
|
+
c.tsw.WriteLinef(" '%s',", interfaceName)
|
|
197
|
+
c.tsw.WriteLinef(" goscript.TypeKind.Interface,")
|
|
198
|
+
c.tsw.WriteLinef(" null,") // Zero value for interface is null
|
|
199
|
+
c.tsw.WriteLinef(" new Set([%s]),", c.collectInterfaceMethods(t))
|
|
200
|
+
c.tsw.WriteLinef(" undefined")
|
|
201
|
+
c.tsw.WriteLinef(");")
|
|
93
202
|
default:
|
|
94
203
|
// type alias
|
|
95
204
|
c.tsw.WriteLiterally("type ")
|
|
@@ -254,3 +363,67 @@ func (c *GoToTSCompiler) WriteImportSpec(a *ast.ImportSpec) {
|
|
|
254
363
|
|
|
255
364
|
c.tsw.WriteImport(impName, importPath)
|
|
256
365
|
}
|
|
366
|
+
|
|
367
|
+
// WriteInterfaceMethodSignature writes a TypeScript interface method signature from a Go ast.Field.
|
|
368
|
+
func (c *GoToTSCompiler) WriteInterfaceMethodSignature(field *ast.Field) error {
|
|
369
|
+
if len(field.Names) == 0 {
|
|
370
|
+
// Should not happen for named methods in an interface, but handle defensively
|
|
371
|
+
return fmt.Errorf("interface method field has no name")
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
methodName := field.Names[0]
|
|
375
|
+
funcType, ok := field.Type.(*ast.FuncType)
|
|
376
|
+
if !ok {
|
|
377
|
+
// Should not happen for valid interface methods, but handle defensively
|
|
378
|
+
c.tsw.WriteCommentInline("unexpected interface method type")
|
|
379
|
+
return fmt.Errorf("interface method type is not a FuncType")
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Write method name
|
|
383
|
+
c.WriteIdentValue(methodName)
|
|
384
|
+
|
|
385
|
+
// Write parameter list (name: type)
|
|
386
|
+
c.tsw.WriteLiterally("(")
|
|
387
|
+
if funcType.Params != nil {
|
|
388
|
+
for i, param := range funcType.Params.List {
|
|
389
|
+
if i > 0 {
|
|
390
|
+
c.tsw.WriteLiterally(", ")
|
|
391
|
+
}
|
|
392
|
+
// Determine parameter name
|
|
393
|
+
paramName := fmt.Sprintf("_p%d", i) // Default placeholder
|
|
394
|
+
if len(param.Names) > 0 && param.Names[0].Name != "" && param.Names[0].Name != "_" {
|
|
395
|
+
paramName = param.Names[0].Name
|
|
396
|
+
}
|
|
397
|
+
c.tsw.WriteLiterally(paramName)
|
|
398
|
+
c.tsw.WriteLiterally(": ")
|
|
399
|
+
c.WriteTypeExpr(param.Type)
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
c.tsw.WriteLiterally(")")
|
|
403
|
+
|
|
404
|
+
// Write return type
|
|
405
|
+
// Use WriteFuncType's logic for return types, but without the async handling
|
|
406
|
+
if funcType.Results != nil && len(funcType.Results.List) > 0 {
|
|
407
|
+
c.tsw.WriteLiterally(": ")
|
|
408
|
+
if len(funcType.Results.List) == 1 && len(funcType.Results.List[0].Names) == 0 {
|
|
409
|
+
// Single unnamed return type
|
|
410
|
+
c.WriteTypeExpr(funcType.Results.List[0].Type)
|
|
411
|
+
} else {
|
|
412
|
+
// Multiple or named return types -> tuple
|
|
413
|
+
c.tsw.WriteLiterally("[")
|
|
414
|
+
for i, result := range funcType.Results.List {
|
|
415
|
+
if i > 0 {
|
|
416
|
+
c.tsw.WriteLiterally(", ")
|
|
417
|
+
}
|
|
418
|
+
c.WriteTypeExpr(result.Type)
|
|
419
|
+
}
|
|
420
|
+
c.tsw.WriteLiterally("]")
|
|
421
|
+
}
|
|
422
|
+
} else {
|
|
423
|
+
// No return value -> void
|
|
424
|
+
c.tsw.WriteLiterally(": void")
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
c.tsw.WriteLine(";") // Semicolon at the end of the method signature
|
|
428
|
+
return nil
|
|
429
|
+
}
|