goscript 0.0.10 → 0.0.13
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 +3 -3
- package/builtin/builtin.go +11 -0
- package/builtin/builtin.ts +170 -65
- package/cmd/goscript/cmd_compile.go +28 -8
- package/compiler/compile.go +42 -34
- package/compiler/compile_decls.go +12 -0
- package/compiler/compile_expr.go +135 -8
- package/compiler/compile_field.go +22 -1
- package/compiler/compile_spec.go +141 -4
- package/compiler/compile_stmt.go +124 -43
- package/compiler/compiler.go +1 -0
- package/compiler/config.go +2 -0
- package/compiler/file_compiler.go +1 -1
- package/compiler/index.test.ts +1 -1
- package/compiler/output_path.go +1 -1
- package/compiler/types/tokens.go +1 -0
- package/compiler/writer.go +1 -1
- package/dist/builtin/builtin.d.ts +63 -1
- package/dist/builtin/builtin.js +81 -0
- package/dist/builtin/builtin.js.map +1 -1
- package/dist/compliance/tests/async_basic/async_basic.gs.js +1 -1
- package/dist/compliance/tests/async_basic/async_basic.gs.js.map +1 -1
- package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.d.ts +1 -0
- package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.js +82 -0
- package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.js.map +1 -0
- package/dist/compliance/tests/channel_basic/channel_basic.gs.js +1 -1
- package/dist/compliance/tests/channel_basic/channel_basic.gs.js.map +1 -1
- package/dist/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.js +1 -1
- 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 +1 -1
- package/dist/compliance/tests/copy_independence/copy_independence.gs.js.map +1 -1
- package/dist/compliance/tests/defer_statement/defer_statement.gs.d.ts +1 -0
- package/dist/compliance/tests/defer_statement/defer_statement.gs.js +75 -0
- package/dist/compliance/tests/defer_statement/defer_statement.gs.js.map +1 -0
- package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.js +3 -3
- package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.js.map +1 -1
- package/dist/compliance/tests/flag_bitwise_op/flag_bitwise_op.gs.d.ts +1 -0
- package/dist/compliance/tests/flag_bitwise_op/flag_bitwise_op.gs.js +29 -0
- package/dist/compliance/tests/flag_bitwise_op/flag_bitwise_op.gs.js.map +1 -0
- package/dist/compliance/tests/for_loop_infinite/for_loop_infinite.gs.d.ts +1 -0
- package/dist/compliance/tests/for_loop_infinite/for_loop_infinite.gs.js +14 -0
- package/dist/compliance/tests/for_loop_infinite/for_loop_infinite.gs.js.map +1 -0
- package/dist/compliance/tests/for_range/for_range.gs.js +1 -1
- package/dist/compliance/tests/for_range/for_range.gs.js.map +1 -1
- package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.d.ts +1 -1
- package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.js +1 -1
- package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.js.map +1 -1
- package/dist/compliance/tests/interface_method_comments/interface_method_comments.gs.d.ts +1 -0
- package/dist/compliance/tests/interface_method_comments/interface_method_comments.gs.js +12 -0
- package/dist/compliance/tests/interface_method_comments/interface_method_comments.gs.js.map +1 -0
- package/dist/compliance/tests/interface_multi_param_return/interface_multi_param_return.gs.js +2 -2
- package/dist/compliance/tests/interface_multi_param_return/interface_multi_param_return.gs.js.map +1 -1
- package/dist/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.js +3 -3
- 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 +13 -3
- package/dist/compliance/tests/interface_type_assertion/interface_type_assertion.gs.js.map +1 -1
- package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.d.ts +1 -0
- package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.js +51 -0
- package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.js.map +1 -0
- package/dist/compliance/tests/map_support/map_support.gs.js +1 -1
- package/dist/compliance/tests/map_support/map_support.gs.js.map +1 -1
- package/dist/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.js +1 -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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -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 +1 -1
- 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 +2 -1
- 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 +2 -4
- 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 +1 -1
- package/dist/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.js.map +1 -1
- package/dist/compliance/tests/slices/slices.gs.js +281 -8
- package/dist/compliance/tests/slices/slices.gs.js.map +1 -1
- package/dist/compliance/tests/string_conversion/string_conversion.gs.d.ts +1 -0
- package/dist/compliance/tests/string_conversion/string_conversion.gs.js +41 -0
- package/dist/compliance/tests/string_conversion/string_conversion.gs.js.map +1 -0
- package/dist/compliance/tests/struct_embedding/struct_embedding.gs.d.ts +1 -0
- package/dist/compliance/tests/struct_embedding/struct_embedding.gs.js +48 -0
- package/dist/compliance/tests/struct_embedding/struct_embedding.gs.js.map +1 -0
- package/dist/compliance/tests/struct_field_access/struct_field_access.gs.js +1 -1
- package/dist/compliance/tests/struct_field_access/struct_field_access.gs.js.map +1 -1
- package/dist/compliance/tests/struct_pointer_interface_fields/struct_pointer_interface_fields.gs.d.ts +1 -0
- package/dist/compliance/tests/struct_pointer_interface_fields/struct_pointer_interface_fields.gs.js +26 -0
- package/dist/compliance/tests/struct_pointer_interface_fields/struct_pointer_interface_fields.gs.js.map +1 -0
- package/dist/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.js +1 -1
- 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 +1 -1
- package/dist/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.js.map +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -306,7 +306,7 @@ func main() {
|
|
|
306
306
|
Generated with `goscript compile .`:
|
|
307
307
|
|
|
308
308
|
```typescript
|
|
309
|
-
import * as goscript from "@
|
|
309
|
+
import * as goscript from "@goscript/builtin"
|
|
310
310
|
|
|
311
311
|
class MyStruct {
|
|
312
312
|
// MyInt is a public integer field, initialized to zero.
|
|
@@ -460,10 +460,10 @@ export async function main(): Promise<void> {
|
|
|
460
460
|
|
|
461
461
|
Code is compiled with `GOARCH=js` and uses a 32-bit environment similar to wasm.
|
|
462
462
|
|
|
463
|
-
All Go import paths are prefixed with `@
|
|
463
|
+
All Go import paths are prefixed with `@goscript/` and can be imported in TypeScript:
|
|
464
464
|
|
|
465
465
|
```typescript
|
|
466
|
-
import { MyAsyncFunction, MyStruct } from '@
|
|
466
|
+
import { MyAsyncFunction, MyStruct } from '@goscript/github.com/myorg/mypackage';
|
|
467
467
|
|
|
468
468
|
// Example of importing and using a compiled Go async function
|
|
469
469
|
async function runGoCode() {
|
package/builtin/builtin.ts
CHANGED
|
@@ -13,6 +13,34 @@ export const makeSlice = <T>(
|
|
|
13
13
|
return slice
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Creates a new slice header that shares the backing array.
|
|
18
|
+
* Arguments mirror Go semantics; omitted indices are undefined.
|
|
19
|
+
*
|
|
20
|
+
* @param arr The original slice/array produced by makeSlice or another slice
|
|
21
|
+
* @param low Starting index (defaults to 0)
|
|
22
|
+
* @param high Ending index (defaults to arr.length)
|
|
23
|
+
* @param max Capacity limit (defaults to original capacity)
|
|
24
|
+
*/
|
|
25
|
+
export const slice = <T>(
|
|
26
|
+
arr: Array<T> & { __capacity?: number },
|
|
27
|
+
low?: number,
|
|
28
|
+
high?: number,
|
|
29
|
+
max?: number,
|
|
30
|
+
): Array<T> & { __capacity?: number } => {
|
|
31
|
+
const start = low ?? 0
|
|
32
|
+
const origLen = arr.length
|
|
33
|
+
const origCap = arr.__capacity !== undefined ? arr.__capacity : origLen
|
|
34
|
+
const end = high !== undefined ? high : origLen
|
|
35
|
+
const newCap = max !== undefined ? max - start : origCap - start
|
|
36
|
+
|
|
37
|
+
const newArr = arr.slice(start, end) as Array<T> & {
|
|
38
|
+
__capacity?: number
|
|
39
|
+
}
|
|
40
|
+
newArr.__capacity = newCap
|
|
41
|
+
return newArr
|
|
42
|
+
}
|
|
43
|
+
|
|
16
44
|
/**
|
|
17
45
|
* Creates a new map (TypeScript Map).
|
|
18
46
|
* @returns A new TypeScript Map.
|
|
@@ -50,8 +78,8 @@ export const cap = <T>(slice: Array<T> & { __capacity?: number }): number => {
|
|
|
50
78
|
* Represents the Go error type (interface).
|
|
51
79
|
*/
|
|
52
80
|
export type Error = {
|
|
53
|
-
Error(): string
|
|
54
|
-
} | null
|
|
81
|
+
Error(): string
|
|
82
|
+
} | null
|
|
55
83
|
|
|
56
84
|
/**
|
|
57
85
|
* Converts a string to an array of Unicode code points (runes).
|
|
@@ -62,6 +90,15 @@ export const stringToRunes = (str: string): number[] => {
|
|
|
62
90
|
return Array.from(str).map((c) => c.codePointAt(0) || 0)
|
|
63
91
|
}
|
|
64
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Converts an array of Unicode code points (runes) to a string.
|
|
95
|
+
* @param runes The input array of numbers representing Unicode code points.
|
|
96
|
+
* @returns The resulting string.
|
|
97
|
+
*/
|
|
98
|
+
export const runesToString = (runes: number[]): string => {
|
|
99
|
+
return String.fromCharCode(...runes);
|
|
100
|
+
};
|
|
101
|
+
|
|
65
102
|
/**
|
|
66
103
|
* Gets a value from a map, with a default value if the key doesn't exist.
|
|
67
104
|
* @param map The map to get from.
|
|
@@ -113,8 +150,14 @@ export const mapHas = <K, V>(map: Map<K, V>, key: K): boolean => {
|
|
|
113
150
|
* @param elements The elements to append.
|
|
114
151
|
* @returns The modified slice (TypeScript array).
|
|
115
152
|
*/
|
|
116
|
-
export const append = <T>(
|
|
153
|
+
export const append = <T>(
|
|
154
|
+
slice: Array<T> & { __capacity?: number },
|
|
155
|
+
...elements: T[]
|
|
156
|
+
): Array<T> & { __capacity?: number } => {
|
|
117
157
|
slice.push(...elements)
|
|
158
|
+
if (slice.__capacity !== undefined && slice.length > slice.__capacity) {
|
|
159
|
+
slice.__capacity = slice.length
|
|
160
|
+
}
|
|
118
161
|
return slice
|
|
119
162
|
}
|
|
120
163
|
|
|
@@ -122,35 +165,35 @@ export const append = <T>(slice: Array<T>, ...elements: T[]): Array<T> => {
|
|
|
122
165
|
* Represents the kinds of Go types that can be registered at runtime.
|
|
123
166
|
*/
|
|
124
167
|
export enum TypeKind {
|
|
125
|
-
Struct =
|
|
126
|
-
Interface =
|
|
127
|
-
Basic =
|
|
128
|
-
Pointer =
|
|
129
|
-
Slice =
|
|
130
|
-
Map =
|
|
131
|
-
Channel =
|
|
132
|
-
Function =
|
|
168
|
+
Struct = 'struct',
|
|
169
|
+
Interface = 'interface',
|
|
170
|
+
Basic = 'basic',
|
|
171
|
+
Pointer = 'pointer',
|
|
172
|
+
Slice = 'slice',
|
|
173
|
+
Map = 'map',
|
|
174
|
+
Channel = 'channel',
|
|
175
|
+
Function = 'function',
|
|
133
176
|
}
|
|
134
177
|
|
|
135
178
|
/**
|
|
136
179
|
* Represents type information for a Go type in the runtime.
|
|
137
180
|
*/
|
|
138
181
|
export interface TypeInfo {
|
|
139
|
-
name: string
|
|
140
|
-
kind: TypeKind
|
|
141
|
-
zeroValue: any
|
|
182
|
+
name: string
|
|
183
|
+
kind: TypeKind
|
|
184
|
+
zeroValue: any
|
|
142
185
|
// For interfaces, the set of methods
|
|
143
|
-
methods?: Set<string
|
|
186
|
+
methods?: Set<string>
|
|
144
187
|
// For structs, the constructor
|
|
145
|
-
constructor?: new (...args: any[]) => any
|
|
188
|
+
constructor?: new (...args: any[]) => any
|
|
146
189
|
}
|
|
147
190
|
|
|
148
191
|
// Registry to store runtime type information
|
|
149
|
-
const typeRegistry = new Map<string, TypeInfo>()
|
|
192
|
+
const typeRegistry = new Map<string, TypeInfo>()
|
|
150
193
|
|
|
151
194
|
/**
|
|
152
195
|
* Registers a type with the runtime type system.
|
|
153
|
-
*
|
|
196
|
+
*
|
|
154
197
|
* @param name The name of the type.
|
|
155
198
|
* @param kind The kind of the type.
|
|
156
199
|
* @param zeroValue The zero value for the type.
|
|
@@ -163,7 +206,7 @@ export const registerType = (
|
|
|
163
206
|
kind: TypeKind,
|
|
164
207
|
zeroValue: any,
|
|
165
208
|
methods?: Set<string>,
|
|
166
|
-
constructor?: new (...args: any[]) => any
|
|
209
|
+
constructor?: new (...args: any[]) => any,
|
|
167
210
|
): TypeInfo => {
|
|
168
211
|
const typeInfo: TypeInfo = {
|
|
169
212
|
name,
|
|
@@ -171,37 +214,40 @@ export const registerType = (
|
|
|
171
214
|
zeroValue,
|
|
172
215
|
methods,
|
|
173
216
|
constructor,
|
|
174
|
-
}
|
|
175
|
-
typeRegistry.set(name, typeInfo)
|
|
176
|
-
return typeInfo
|
|
177
|
-
}
|
|
217
|
+
}
|
|
218
|
+
typeRegistry.set(name, typeInfo)
|
|
219
|
+
return typeInfo
|
|
220
|
+
}
|
|
178
221
|
|
|
179
222
|
/**
|
|
180
223
|
* Represents the result of a type assertion.
|
|
181
224
|
*/
|
|
182
225
|
export interface TypeAssertResult<T> {
|
|
183
|
-
value: T
|
|
184
|
-
ok: boolean
|
|
226
|
+
value: T
|
|
227
|
+
ok: boolean
|
|
185
228
|
}
|
|
186
229
|
|
|
187
230
|
/**
|
|
188
231
|
* Performs a type assertion at runtime.
|
|
189
|
-
*
|
|
232
|
+
*
|
|
190
233
|
* @param value The value to assert.
|
|
191
234
|
* @param typeName The name of the target type.
|
|
192
235
|
* @returns An object with the asserted value and whether the assertion succeeded.
|
|
193
236
|
*/
|
|
194
|
-
export function typeAssert<T>(
|
|
237
|
+
export function typeAssert<T>(
|
|
238
|
+
value: any,
|
|
239
|
+
typeName: string,
|
|
240
|
+
): TypeAssertResult<T> {
|
|
195
241
|
// Get the type information from the registry
|
|
196
|
-
const typeInfo = typeRegistry.get(typeName)
|
|
242
|
+
const typeInfo = typeRegistry.get(typeName)
|
|
197
243
|
if (!typeInfo) {
|
|
198
|
-
console.warn(`Type information for '${typeName}' not found in registry.`)
|
|
199
|
-
return { value: null as unknown as T, ok: false }
|
|
244
|
+
console.warn(`Type information for '${typeName}' not found in registry.`)
|
|
245
|
+
return { value: null as unknown as T, ok: false }
|
|
200
246
|
}
|
|
201
247
|
|
|
202
248
|
// If value is null or undefined, assertion fails
|
|
203
249
|
if (value === null || value === undefined) {
|
|
204
|
-
return { value: typeInfo.zeroValue as T, ok: false }
|
|
250
|
+
return { value: typeInfo.zeroValue as T, ok: false }
|
|
205
251
|
}
|
|
206
252
|
|
|
207
253
|
// Check based on the kind of the target type
|
|
@@ -209,86 +255,88 @@ export function typeAssert<T>(value: any, typeName: string): TypeAssertResult<T>
|
|
|
209
255
|
case TypeKind.Struct:
|
|
210
256
|
// For structs, use instanceof with the constructor
|
|
211
257
|
if (typeInfo.constructor && value instanceof typeInfo.constructor) {
|
|
212
|
-
return { value: value as T, ok: true }
|
|
258
|
+
return { value: value as T, ok: true }
|
|
213
259
|
}
|
|
214
|
-
break
|
|
215
|
-
|
|
260
|
+
break
|
|
261
|
+
|
|
216
262
|
case TypeKind.Interface:
|
|
217
263
|
// For interfaces, check if the value has all the required methods
|
|
218
264
|
if (typeInfo.methods && typeof value === 'object') {
|
|
219
265
|
const allMethodsPresent = Array.from(typeInfo.methods).every(
|
|
220
|
-
(method) => typeof (value as any)[method] === 'function'
|
|
221
|
-
)
|
|
266
|
+
(method) => typeof (value as any)[method] === 'function',
|
|
267
|
+
)
|
|
222
268
|
if (allMethodsPresent) {
|
|
223
|
-
return { value: value as T, ok: true }
|
|
269
|
+
return { value: value as T, ok: true }
|
|
224
270
|
}
|
|
225
271
|
}
|
|
226
|
-
break
|
|
227
|
-
|
|
272
|
+
break
|
|
273
|
+
|
|
228
274
|
case TypeKind.Basic:
|
|
229
275
|
// For basic types, check if the value matches the expected JavaScript type
|
|
230
276
|
// This is a simple check for common basic types
|
|
231
|
-
const basicType = typeof value
|
|
277
|
+
const basicType = typeof value
|
|
232
278
|
if (
|
|
233
|
-
basicType === 'string' ||
|
|
234
|
-
basicType === 'number' ||
|
|
279
|
+
basicType === 'string' ||
|
|
280
|
+
basicType === 'number' ||
|
|
235
281
|
basicType === 'boolean'
|
|
236
282
|
) {
|
|
237
|
-
return { value: value as T, ok: true }
|
|
283
|
+
return { value: value as T, ok: true }
|
|
238
284
|
}
|
|
239
|
-
break
|
|
240
|
-
|
|
285
|
+
break
|
|
286
|
+
|
|
241
287
|
case TypeKind.Pointer:
|
|
242
288
|
// For pointers, check if value is not null or undefined
|
|
243
289
|
// In Go, pointers can be nil which we represent as null/undefined in TS
|
|
244
290
|
if (value !== null && value !== undefined) {
|
|
245
|
-
return { value: value as T, ok: true }
|
|
291
|
+
return { value: value as T, ok: true }
|
|
246
292
|
}
|
|
247
|
-
break
|
|
248
|
-
|
|
293
|
+
break
|
|
294
|
+
|
|
249
295
|
case TypeKind.Slice:
|
|
250
296
|
// For slices, check if the value is an array
|
|
251
297
|
if (Array.isArray(value)) {
|
|
252
|
-
return { value: value as T, ok: true }
|
|
298
|
+
return { value: value as T, ok: true }
|
|
253
299
|
}
|
|
254
|
-
break
|
|
255
|
-
|
|
300
|
+
break
|
|
301
|
+
|
|
256
302
|
case TypeKind.Map:
|
|
257
303
|
// For maps, check if the value is a Map
|
|
258
304
|
if (value instanceof Map) {
|
|
259
|
-
return { value: value as T, ok: true }
|
|
305
|
+
return { value: value as T, ok: true }
|
|
260
306
|
}
|
|
261
|
-
break
|
|
262
|
-
|
|
307
|
+
break
|
|
308
|
+
|
|
263
309
|
case TypeKind.Channel:
|
|
264
310
|
// For channels, check if the value has the required Channel interface methods
|
|
265
311
|
if (
|
|
266
|
-
typeof value === 'object' &&
|
|
312
|
+
typeof value === 'object' &&
|
|
267
313
|
value !== null &&
|
|
268
|
-
'send' in value &&
|
|
269
|
-
'receive' in value &&
|
|
314
|
+
'send' in value &&
|
|
315
|
+
'receive' in value &&
|
|
270
316
|
'close' in value &&
|
|
271
317
|
typeof value.send === 'function' &&
|
|
272
318
|
typeof value.receive === 'function' &&
|
|
273
319
|
typeof value.close === 'function'
|
|
274
320
|
) {
|
|
275
|
-
return { value: value as T, ok: true }
|
|
321
|
+
return { value: value as T, ok: true }
|
|
276
322
|
}
|
|
277
|
-
break
|
|
278
|
-
|
|
323
|
+
break
|
|
324
|
+
|
|
279
325
|
case TypeKind.Function:
|
|
280
326
|
// For functions, check if the value is a function
|
|
281
327
|
if (typeof value === 'function') {
|
|
282
|
-
return { value: value as T, ok: true }
|
|
328
|
+
return { value: value as T, ok: true }
|
|
283
329
|
}
|
|
284
|
-
break
|
|
285
|
-
|
|
330
|
+
break
|
|
331
|
+
|
|
286
332
|
default:
|
|
287
|
-
console.warn(
|
|
333
|
+
console.warn(
|
|
334
|
+
`Type assertion for kind '${typeInfo.kind}' not implemented.`,
|
|
335
|
+
)
|
|
288
336
|
}
|
|
289
337
|
|
|
290
338
|
// Assertion failed
|
|
291
|
-
return { value: typeInfo.zeroValue as T, ok: false }
|
|
339
|
+
return { value: typeInfo.zeroValue as T, ok: false }
|
|
292
340
|
}
|
|
293
341
|
|
|
294
342
|
/**
|
|
@@ -715,3 +763,60 @@ export const makeChannel = <T>(
|
|
|
715
763
|
): Channel<T> => {
|
|
716
764
|
return new BufferedChannel<T>(bufferSize, zeroValue)
|
|
717
765
|
}
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* DisposableStack manages synchronous disposable resources, mimicking Go's defer behavior.
|
|
769
|
+
* Functions added via `defer` are executed in LIFO order when the stack is disposed.
|
|
770
|
+
* Implements the `Disposable` interface for use with `using` declarations.
|
|
771
|
+
*/
|
|
772
|
+
export class DisposableStack implements Disposable {
|
|
773
|
+
#stack: (() => void)[] = [];
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Adds a function to be executed when the stack is disposed.
|
|
777
|
+
* @param fn The function to defer.
|
|
778
|
+
*/
|
|
779
|
+
defer(fn: () => void): void { this.#stack.push(fn); }
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* Disposes of the resources in the stack by executing the deferred functions
|
|
783
|
+
* in Last-In, First-Out (LIFO) order.
|
|
784
|
+
* If a deferred function throws an error, disposal stops, and the error is rethrown,
|
|
785
|
+
* similar to Go's panic behavior during defer execution.
|
|
786
|
+
*/
|
|
787
|
+
[Symbol.dispose](): void {
|
|
788
|
+
// Emulate Go: if a deferred throws, stop and rethrow
|
|
789
|
+
while (this.#stack.length) {
|
|
790
|
+
const fn = this.#stack.pop()!;
|
|
791
|
+
fn();
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* AsyncDisposableStack manages asynchronous disposable resources, mimicking Go's defer behavior.
|
|
798
|
+
* Functions added via `defer` are executed sequentially in LIFO order when the stack is disposed.
|
|
799
|
+
* Implements the `AsyncDisposable` interface for use with `await using` declarations.
|
|
800
|
+
*/
|
|
801
|
+
export class AsyncDisposableStack implements AsyncDisposable {
|
|
802
|
+
#stack: (() => Promise<void> | void)[] = [];
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* Adds a synchronous or asynchronous function to be executed when the stack is disposed.
|
|
806
|
+
* @param fn The function to defer. Can return void or a Promise<void>.
|
|
807
|
+
*/
|
|
808
|
+
defer(fn: () => Promise<void> | void): void {
|
|
809
|
+
this.#stack.push(fn);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* Asynchronously disposes of the resources in the stack by executing the deferred functions
|
|
814
|
+
* sequentially in Last-In, First-Out (LIFO) order. It awaits each function if it returns a promise.
|
|
815
|
+
*/
|
|
816
|
+
async [Symbol.asyncDispose](): Promise<void> {
|
|
817
|
+
// Execute in LIFO order, awaiting each potentially async function
|
|
818
|
+
for (let i = this.#stack.length - 1; i >= 0; --i) {
|
|
819
|
+
await this.#stack[i]();
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
@@ -2,17 +2,22 @@ package main
|
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
4
|
"context"
|
|
5
|
+
"slices"
|
|
5
6
|
|
|
6
7
|
"github.com/aperturerobotics/cli"
|
|
7
8
|
"github.com/paralin/goscript/compiler"
|
|
8
9
|
"github.com/pkg/errors"
|
|
9
10
|
"github.com/sirupsen/logrus"
|
|
11
|
+
|
|
12
|
+
// _ ensure we include the builtin package
|
|
13
|
+
_ "github.com/paralin/goscript/builtin"
|
|
10
14
|
)
|
|
11
15
|
|
|
12
16
|
var (
|
|
13
|
-
cliCompiler
|
|
14
|
-
cliCompilerConfig
|
|
15
|
-
cliCompilerPkg
|
|
17
|
+
cliCompiler *compiler.Compiler
|
|
18
|
+
cliCompilerConfig compiler.Config
|
|
19
|
+
cliCompilerPkg cli.StringSlice
|
|
20
|
+
cliCompilerBuildFlags cli.StringSlice
|
|
16
21
|
)
|
|
17
22
|
|
|
18
23
|
// CompileCommands are commands related to compiling code.
|
|
@@ -29,9 +34,11 @@ var CompileCommands = []*cli.Command{{
|
|
|
29
34
|
return
|
|
30
35
|
},
|
|
31
36
|
Flags: []cli.Flag{
|
|
32
|
-
&cli.
|
|
37
|
+
&cli.StringSliceFlag{
|
|
33
38
|
Name: "package",
|
|
34
|
-
Usage: "the package to compile",
|
|
39
|
+
Usage: "the package(s) to compile",
|
|
40
|
+
Aliases: []string{"p", "packages"},
|
|
41
|
+
EnvVars: []string{"GOSCRIPT_PACKAGES"},
|
|
35
42
|
Destination: &cliCompilerPkg,
|
|
36
43
|
},
|
|
37
44
|
&cli.StringFlag{
|
|
@@ -39,21 +46,34 @@ var CompileCommands = []*cli.Command{{
|
|
|
39
46
|
Usage: "the output typescript path to use",
|
|
40
47
|
Destination: &cliCompilerConfig.OutputPathRoot,
|
|
41
48
|
Value: "./output",
|
|
49
|
+
EnvVars: []string{"GOSCRIPT_OUTPUT"},
|
|
42
50
|
},
|
|
43
51
|
&cli.StringFlag{
|
|
44
52
|
Name: "dir",
|
|
45
53
|
Usage: "the working directory to use for the compiler (default: current directory)",
|
|
46
54
|
Destination: &cliCompilerConfig.Dir,
|
|
47
55
|
Value: "",
|
|
56
|
+
EnvVars: []string{"GOSCRIPT_DIR"},
|
|
57
|
+
},
|
|
58
|
+
&cli.StringSliceFlag{
|
|
59
|
+
Name: "build-flags",
|
|
60
|
+
Aliases: []string{"b", "buildflags", "build-flag", "buildflag"},
|
|
61
|
+
Usage: "Go build flags (tags) to use during analysis",
|
|
62
|
+
Destination: &cliCompilerBuildFlags,
|
|
63
|
+
EnvVars: []string{"GOSCRIPT_BUILD_FLAGS"},
|
|
48
64
|
},
|
|
49
65
|
},
|
|
50
66
|
}}
|
|
51
67
|
|
|
52
68
|
// compilePackage tries to compile the package.
|
|
53
69
|
func compilePackage(c *cli.Context) error {
|
|
54
|
-
|
|
55
|
-
|
|
70
|
+
pkgs := cliCompilerPkg.Value()
|
|
71
|
+
if len(pkgs) == 0 {
|
|
72
|
+
return errors.New("package(s) must be specified")
|
|
56
73
|
}
|
|
57
74
|
|
|
58
|
-
|
|
75
|
+
// build flags
|
|
76
|
+
cliCompilerConfig.BuildFlags = slices.Clone(cliCompilerBuildFlags.Value())
|
|
77
|
+
|
|
78
|
+
return cliCompiler.CompilePackages(context.Background(), pkgs...)
|
|
59
79
|
}
|
package/compiler/compile.go
CHANGED
|
@@ -4,7 +4,7 @@ import (
|
|
|
4
4
|
"fmt"
|
|
5
5
|
"go/ast"
|
|
6
6
|
"go/token"
|
|
7
|
-
"go/types"
|
|
7
|
+
gtypes "go/types"
|
|
8
8
|
|
|
9
9
|
gstypes "github.com/paralin/goscript/compiler/types"
|
|
10
10
|
"golang.org/x/tools/go/packages"
|
|
@@ -12,19 +12,19 @@ import (
|
|
|
12
12
|
|
|
13
13
|
// GoToTSCompiler compiles Go code to TypeScript code.
|
|
14
14
|
type GoToTSCompiler struct {
|
|
15
|
-
tsw
|
|
16
|
-
imports
|
|
17
|
-
pkg
|
|
18
|
-
cmap
|
|
19
|
-
asyncFuncs
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
tsw *TSCodeWriter
|
|
16
|
+
imports map[string]*fileImport
|
|
17
|
+
pkg *packages.Package
|
|
18
|
+
cmap ast.CommentMap
|
|
19
|
+
asyncFuncs map[string]bool // Track which functions are async
|
|
20
|
+
nextBlockNeedsDefer bool // Track if the next block should have a "using" statement
|
|
21
|
+
inAsyncFunction bool // Track if we're inside an async function
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
// WriteGoType writes a Go type as a TypeScript type.
|
|
25
|
-
func (c *GoToTSCompiler) WriteGoType(typ
|
|
25
|
+
func (c *GoToTSCompiler) WriteGoType(typ gtypes.Type) {
|
|
26
26
|
switch t := typ.(type) {
|
|
27
|
-
case *
|
|
27
|
+
case *gtypes.Basic:
|
|
28
28
|
// Handle basic types (int, string, etc.)
|
|
29
29
|
name := t.Name()
|
|
30
30
|
if tsType, ok := gstypes.GoBuiltinToTypescript(name); ok {
|
|
@@ -32,31 +32,31 @@ func (c *GoToTSCompiler) WriteGoType(typ types.Type) {
|
|
|
32
32
|
} else {
|
|
33
33
|
c.tsw.WriteLiterally(name)
|
|
34
34
|
}
|
|
35
|
-
case *
|
|
35
|
+
case *gtypes.Named:
|
|
36
36
|
// Handle named types (custom types)
|
|
37
37
|
c.tsw.WriteLiterally(t.Obj().Name())
|
|
38
|
-
case *
|
|
38
|
+
case *gtypes.Pointer:
|
|
39
39
|
// Handle pointer types (*T becomes T | null)
|
|
40
40
|
c.WriteGoType(t.Elem())
|
|
41
41
|
c.tsw.WriteLiterally(" | null")
|
|
42
|
-
case *
|
|
42
|
+
case *gtypes.Slice, *gtypes.Array:
|
|
43
43
|
// Handle array/slice types ([]T or [N]T becomes T[])
|
|
44
|
-
var elemType
|
|
45
|
-
if slice, ok := t.(*
|
|
44
|
+
var elemType gtypes.Type
|
|
45
|
+
if slice, ok := t.(*gtypes.Slice); ok {
|
|
46
46
|
elemType = slice.Elem()
|
|
47
|
-
} else if array, ok := t.(*
|
|
47
|
+
} else if array, ok := t.(*gtypes.Array); ok {
|
|
48
48
|
elemType = array.Elem()
|
|
49
49
|
}
|
|
50
50
|
c.WriteGoType(elemType)
|
|
51
51
|
c.tsw.WriteLiterally("[]")
|
|
52
|
-
case *
|
|
52
|
+
case *gtypes.Map:
|
|
53
53
|
// Handle map types (map[K]V becomes Map<K, V>)
|
|
54
54
|
c.tsw.WriteLiterally("Map<")
|
|
55
55
|
c.WriteGoType(t.Key())
|
|
56
56
|
c.tsw.WriteLiterally(", ")
|
|
57
57
|
c.WriteGoType(t.Elem())
|
|
58
58
|
c.tsw.WriteLiterally(">")
|
|
59
|
-
case *
|
|
59
|
+
case *gtypes.Chan:
|
|
60
60
|
// Handle channel types (chan T becomes goscript.Channel<T>)
|
|
61
61
|
c.tsw.WriteLiterally("goscript.Channel<")
|
|
62
62
|
c.WriteGoType(t.Elem())
|
|
@@ -68,24 +68,32 @@ func (c *GoToTSCompiler) WriteGoType(typ types.Type) {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
// scanForDefer checks if a block contains any defer statements (recursively).
|
|
72
|
+
func (c *GoToTSCompiler) scanForDefer(block *ast.BlockStmt) bool {
|
|
73
|
+
hasDefer := false
|
|
74
|
+
ast.Inspect(block, func(n ast.Node) bool {
|
|
75
|
+
if _, ok := n.(*ast.DeferStmt); ok {
|
|
76
|
+
hasDefer = true
|
|
77
|
+
return false // Stop traversal
|
|
78
|
+
}
|
|
79
|
+
return true // Continue traversal
|
|
80
|
+
})
|
|
81
|
+
return hasDefer
|
|
82
|
+
}
|
|
83
|
+
|
|
71
84
|
// NewGoToTSCompiler builds a new GoToTSCompiler
|
|
72
85
|
func NewGoToTSCompiler(tsw *TSCodeWriter, pkg *packages.Package, cmap ast.CommentMap) *GoToTSCompiler {
|
|
73
86
|
return &GoToTSCompiler{
|
|
74
|
-
tsw:
|
|
75
|
-
imports:
|
|
76
|
-
pkg:
|
|
77
|
-
cmap:
|
|
78
|
-
asyncFuncs:
|
|
79
|
-
|
|
87
|
+
tsw: tsw,
|
|
88
|
+
imports: make(map[string]*fileImport),
|
|
89
|
+
pkg: pkg,
|
|
90
|
+
cmap: cmap,
|
|
91
|
+
asyncFuncs: make(map[string]bool),
|
|
92
|
+
nextBlockNeedsDefer: false,
|
|
93
|
+
inAsyncFunction: false,
|
|
80
94
|
}
|
|
81
95
|
}
|
|
82
96
|
|
|
83
|
-
// newTempVar generates a unique temporary variable name.
|
|
84
|
-
func (c *GoToTSCompiler) newTempVar() string {
|
|
85
|
-
c.tempVarCounter++
|
|
86
|
-
return fmt.Sprintf("_tempVar%d", c.tempVarCounter)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
97
|
// isAsyncFunc determines if a function is asynchronous
|
|
90
98
|
// A function is async if it contains channel operations or calls other async functions
|
|
91
99
|
func (c *GoToTSCompiler) isAsyncFunc(name string) bool {
|
|
@@ -128,7 +136,7 @@ func (c *GoToTSCompiler) containsAsyncOperations(node ast.Node) bool {
|
|
|
128
136
|
// Handles array types recursively.
|
|
129
137
|
func (c *GoToTSCompiler) WriteZeroValueForType(typ any) {
|
|
130
138
|
switch t := typ.(type) {
|
|
131
|
-
case *
|
|
139
|
+
case *gtypes.Array:
|
|
132
140
|
c.tsw.WriteLiterally("[")
|
|
133
141
|
for i := 0; i < int(t.Len()); i++ {
|
|
134
142
|
if i > 0 {
|
|
@@ -153,11 +161,11 @@ func (c *GoToTSCompiler) WriteZeroValueForType(typ any) {
|
|
|
153
161
|
c.WriteZeroValueForType(t.Elt)
|
|
154
162
|
}
|
|
155
163
|
c.tsw.WriteLiterally("]")
|
|
156
|
-
case *
|
|
164
|
+
case *gtypes.Basic:
|
|
157
165
|
switch t.Kind() {
|
|
158
|
-
case
|
|
166
|
+
case gtypes.Bool:
|
|
159
167
|
c.tsw.WriteLiterally("false")
|
|
160
|
-
case
|
|
168
|
+
case gtypes.String:
|
|
161
169
|
c.tsw.WriteLiterally(`""`)
|
|
162
170
|
default:
|
|
163
171
|
c.tsw.WriteLiterally("0")
|
|
@@ -65,8 +65,20 @@ func (c *GoToTSCompiler) WriteFuncDeclAsFunction(decl *ast.FuncDecl) error {
|
|
|
65
65
|
// WriteFuncType needs to be aware if the function is async
|
|
66
66
|
c.WriteFuncType(decl.Type, isAsync) // Write signature (params, return type)
|
|
67
67
|
c.tsw.WriteLiterally(" ")
|
|
68
|
+
|
|
69
|
+
// Check if function body has defer statements
|
|
70
|
+
c.nextBlockNeedsDefer = c.scanForDefer(decl.Body)
|
|
71
|
+
|
|
72
|
+
// Save previous async state and set current state based on isAsync
|
|
73
|
+
previousAsyncState := c.inAsyncFunction
|
|
74
|
+
c.inAsyncFunction = isAsync
|
|
75
|
+
|
|
68
76
|
if err := c.WriteStmt(decl.Body); err != nil {
|
|
77
|
+
c.inAsyncFunction = previousAsyncState // Restore state before returning error
|
|
69
78
|
return fmt.Errorf("failed to write function body: %w", err)
|
|
70
79
|
}
|
|
80
|
+
|
|
81
|
+
// Restore previous async state
|
|
82
|
+
c.inAsyncFunction = previousAsyncState
|
|
71
83
|
return nil
|
|
72
84
|
}
|