goscript 0.0.10 → 0.0.11
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 +161 -65
- package/cmd/goscript/cmd_compile.go +8 -6
- package/compiler/compile.go +28 -11
- package/compiler/compile_decls.go +12 -0
- package/compiler/compile_expr.go +59 -2
- package/compiler/compile_spec.go +110 -3
- package/compiler/compile_stmt.go +89 -15
- package/dist/builtin/builtin.d.ts +57 -1
- package/dist/builtin/builtin.js +73 -0
- package/dist/builtin/builtin.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/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 +1 -1
- package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.js.map +1 -1
- 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/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.js +1 -1
- 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 +11 -1
- 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/slices/slices.gs.js +280 -7
- package/dist/compliance/tests/slices/slices.gs.js.map +1 -1
- 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/package.json +4 -3
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).
|
|
@@ -113,8 +141,14 @@ export const mapHas = <K, V>(map: Map<K, V>, key: K): boolean => {
|
|
|
113
141
|
* @param elements The elements to append.
|
|
114
142
|
* @returns The modified slice (TypeScript array).
|
|
115
143
|
*/
|
|
116
|
-
export const append = <T>(
|
|
144
|
+
export const append = <T>(
|
|
145
|
+
slice: Array<T> & { __capacity?: number },
|
|
146
|
+
...elements: T[]
|
|
147
|
+
): Array<T> & { __capacity?: number } => {
|
|
117
148
|
slice.push(...elements)
|
|
149
|
+
if (slice.__capacity !== undefined && slice.length > slice.__capacity) {
|
|
150
|
+
slice.__capacity = slice.length
|
|
151
|
+
}
|
|
118
152
|
return slice
|
|
119
153
|
}
|
|
120
154
|
|
|
@@ -122,35 +156,35 @@ export const append = <T>(slice: Array<T>, ...elements: T[]): Array<T> => {
|
|
|
122
156
|
* Represents the kinds of Go types that can be registered at runtime.
|
|
123
157
|
*/
|
|
124
158
|
export enum TypeKind {
|
|
125
|
-
Struct =
|
|
126
|
-
Interface =
|
|
127
|
-
Basic =
|
|
128
|
-
Pointer =
|
|
129
|
-
Slice =
|
|
130
|
-
Map =
|
|
131
|
-
Channel =
|
|
132
|
-
Function =
|
|
159
|
+
Struct = 'struct',
|
|
160
|
+
Interface = 'interface',
|
|
161
|
+
Basic = 'basic',
|
|
162
|
+
Pointer = 'pointer',
|
|
163
|
+
Slice = 'slice',
|
|
164
|
+
Map = 'map',
|
|
165
|
+
Channel = 'channel',
|
|
166
|
+
Function = 'function',
|
|
133
167
|
}
|
|
134
168
|
|
|
135
169
|
/**
|
|
136
170
|
* Represents type information for a Go type in the runtime.
|
|
137
171
|
*/
|
|
138
172
|
export interface TypeInfo {
|
|
139
|
-
name: string
|
|
140
|
-
kind: TypeKind
|
|
141
|
-
zeroValue: any
|
|
173
|
+
name: string
|
|
174
|
+
kind: TypeKind
|
|
175
|
+
zeroValue: any
|
|
142
176
|
// For interfaces, the set of methods
|
|
143
|
-
methods?: Set<string
|
|
177
|
+
methods?: Set<string>
|
|
144
178
|
// For structs, the constructor
|
|
145
|
-
constructor?: new (...args: any[]) => any
|
|
179
|
+
constructor?: new (...args: any[]) => any
|
|
146
180
|
}
|
|
147
181
|
|
|
148
182
|
// Registry to store runtime type information
|
|
149
|
-
const typeRegistry = new Map<string, TypeInfo>()
|
|
183
|
+
const typeRegistry = new Map<string, TypeInfo>()
|
|
150
184
|
|
|
151
185
|
/**
|
|
152
186
|
* Registers a type with the runtime type system.
|
|
153
|
-
*
|
|
187
|
+
*
|
|
154
188
|
* @param name The name of the type.
|
|
155
189
|
* @param kind The kind of the type.
|
|
156
190
|
* @param zeroValue The zero value for the type.
|
|
@@ -163,7 +197,7 @@ export const registerType = (
|
|
|
163
197
|
kind: TypeKind,
|
|
164
198
|
zeroValue: any,
|
|
165
199
|
methods?: Set<string>,
|
|
166
|
-
constructor?: new (...args: any[]) => any
|
|
200
|
+
constructor?: new (...args: any[]) => any,
|
|
167
201
|
): TypeInfo => {
|
|
168
202
|
const typeInfo: TypeInfo = {
|
|
169
203
|
name,
|
|
@@ -171,37 +205,40 @@ export const registerType = (
|
|
|
171
205
|
zeroValue,
|
|
172
206
|
methods,
|
|
173
207
|
constructor,
|
|
174
|
-
}
|
|
175
|
-
typeRegistry.set(name, typeInfo)
|
|
176
|
-
return typeInfo
|
|
177
|
-
}
|
|
208
|
+
}
|
|
209
|
+
typeRegistry.set(name, typeInfo)
|
|
210
|
+
return typeInfo
|
|
211
|
+
}
|
|
178
212
|
|
|
179
213
|
/**
|
|
180
214
|
* Represents the result of a type assertion.
|
|
181
215
|
*/
|
|
182
216
|
export interface TypeAssertResult<T> {
|
|
183
|
-
value: T
|
|
184
|
-
ok: boolean
|
|
217
|
+
value: T
|
|
218
|
+
ok: boolean
|
|
185
219
|
}
|
|
186
220
|
|
|
187
221
|
/**
|
|
188
222
|
* Performs a type assertion at runtime.
|
|
189
|
-
*
|
|
223
|
+
*
|
|
190
224
|
* @param value The value to assert.
|
|
191
225
|
* @param typeName The name of the target type.
|
|
192
226
|
* @returns An object with the asserted value and whether the assertion succeeded.
|
|
193
227
|
*/
|
|
194
|
-
export function typeAssert<T>(
|
|
228
|
+
export function typeAssert<T>(
|
|
229
|
+
value: any,
|
|
230
|
+
typeName: string,
|
|
231
|
+
): TypeAssertResult<T> {
|
|
195
232
|
// Get the type information from the registry
|
|
196
|
-
const typeInfo = typeRegistry.get(typeName)
|
|
233
|
+
const typeInfo = typeRegistry.get(typeName)
|
|
197
234
|
if (!typeInfo) {
|
|
198
|
-
console.warn(`Type information for '${typeName}' not found in registry.`)
|
|
199
|
-
return { value: null as unknown as T, ok: false }
|
|
235
|
+
console.warn(`Type information for '${typeName}' not found in registry.`)
|
|
236
|
+
return { value: null as unknown as T, ok: false }
|
|
200
237
|
}
|
|
201
238
|
|
|
202
239
|
// If value is null or undefined, assertion fails
|
|
203
240
|
if (value === null || value === undefined) {
|
|
204
|
-
return { value: typeInfo.zeroValue as T, ok: false }
|
|
241
|
+
return { value: typeInfo.zeroValue as T, ok: false }
|
|
205
242
|
}
|
|
206
243
|
|
|
207
244
|
// Check based on the kind of the target type
|
|
@@ -209,86 +246,88 @@ export function typeAssert<T>(value: any, typeName: string): TypeAssertResult<T>
|
|
|
209
246
|
case TypeKind.Struct:
|
|
210
247
|
// For structs, use instanceof with the constructor
|
|
211
248
|
if (typeInfo.constructor && value instanceof typeInfo.constructor) {
|
|
212
|
-
return { value: value as T, ok: true }
|
|
249
|
+
return { value: value as T, ok: true }
|
|
213
250
|
}
|
|
214
|
-
break
|
|
215
|
-
|
|
251
|
+
break
|
|
252
|
+
|
|
216
253
|
case TypeKind.Interface:
|
|
217
254
|
// For interfaces, check if the value has all the required methods
|
|
218
255
|
if (typeInfo.methods && typeof value === 'object') {
|
|
219
256
|
const allMethodsPresent = Array.from(typeInfo.methods).every(
|
|
220
|
-
(method) => typeof (value as any)[method] === 'function'
|
|
221
|
-
)
|
|
257
|
+
(method) => typeof (value as any)[method] === 'function',
|
|
258
|
+
)
|
|
222
259
|
if (allMethodsPresent) {
|
|
223
|
-
return { value: value as T, ok: true }
|
|
260
|
+
return { value: value as T, ok: true }
|
|
224
261
|
}
|
|
225
262
|
}
|
|
226
|
-
break
|
|
227
|
-
|
|
263
|
+
break
|
|
264
|
+
|
|
228
265
|
case TypeKind.Basic:
|
|
229
266
|
// For basic types, check if the value matches the expected JavaScript type
|
|
230
267
|
// This is a simple check for common basic types
|
|
231
|
-
const basicType = typeof value
|
|
268
|
+
const basicType = typeof value
|
|
232
269
|
if (
|
|
233
|
-
basicType === 'string' ||
|
|
234
|
-
basicType === 'number' ||
|
|
270
|
+
basicType === 'string' ||
|
|
271
|
+
basicType === 'number' ||
|
|
235
272
|
basicType === 'boolean'
|
|
236
273
|
) {
|
|
237
|
-
return { value: value as T, ok: true }
|
|
274
|
+
return { value: value as T, ok: true }
|
|
238
275
|
}
|
|
239
|
-
break
|
|
240
|
-
|
|
276
|
+
break
|
|
277
|
+
|
|
241
278
|
case TypeKind.Pointer:
|
|
242
279
|
// For pointers, check if value is not null or undefined
|
|
243
280
|
// In Go, pointers can be nil which we represent as null/undefined in TS
|
|
244
281
|
if (value !== null && value !== undefined) {
|
|
245
|
-
return { value: value as T, ok: true }
|
|
282
|
+
return { value: value as T, ok: true }
|
|
246
283
|
}
|
|
247
|
-
break
|
|
248
|
-
|
|
284
|
+
break
|
|
285
|
+
|
|
249
286
|
case TypeKind.Slice:
|
|
250
287
|
// For slices, check if the value is an array
|
|
251
288
|
if (Array.isArray(value)) {
|
|
252
|
-
return { value: value as T, ok: true }
|
|
289
|
+
return { value: value as T, ok: true }
|
|
253
290
|
}
|
|
254
|
-
break
|
|
255
|
-
|
|
291
|
+
break
|
|
292
|
+
|
|
256
293
|
case TypeKind.Map:
|
|
257
294
|
// For maps, check if the value is a Map
|
|
258
295
|
if (value instanceof Map) {
|
|
259
|
-
return { value: value as T, ok: true }
|
|
296
|
+
return { value: value as T, ok: true }
|
|
260
297
|
}
|
|
261
|
-
break
|
|
262
|
-
|
|
298
|
+
break
|
|
299
|
+
|
|
263
300
|
case TypeKind.Channel:
|
|
264
301
|
// For channels, check if the value has the required Channel interface methods
|
|
265
302
|
if (
|
|
266
|
-
typeof value === 'object' &&
|
|
303
|
+
typeof value === 'object' &&
|
|
267
304
|
value !== null &&
|
|
268
|
-
'send' in value &&
|
|
269
|
-
'receive' in value &&
|
|
305
|
+
'send' in value &&
|
|
306
|
+
'receive' in value &&
|
|
270
307
|
'close' in value &&
|
|
271
308
|
typeof value.send === 'function' &&
|
|
272
309
|
typeof value.receive === 'function' &&
|
|
273
310
|
typeof value.close === 'function'
|
|
274
311
|
) {
|
|
275
|
-
return { value: value as T, ok: true }
|
|
312
|
+
return { value: value as T, ok: true }
|
|
276
313
|
}
|
|
277
|
-
break
|
|
278
|
-
|
|
314
|
+
break
|
|
315
|
+
|
|
279
316
|
case TypeKind.Function:
|
|
280
317
|
// For functions, check if the value is a function
|
|
281
318
|
if (typeof value === 'function') {
|
|
282
|
-
return { value: value as T, ok: true }
|
|
319
|
+
return { value: value as T, ok: true }
|
|
283
320
|
}
|
|
284
|
-
break
|
|
285
|
-
|
|
321
|
+
break
|
|
322
|
+
|
|
286
323
|
default:
|
|
287
|
-
console.warn(
|
|
324
|
+
console.warn(
|
|
325
|
+
`Type assertion for kind '${typeInfo.kind}' not implemented.`,
|
|
326
|
+
)
|
|
288
327
|
}
|
|
289
328
|
|
|
290
329
|
// Assertion failed
|
|
291
|
-
return { value: typeInfo.zeroValue as T, ok: false }
|
|
330
|
+
return { value: typeInfo.zeroValue as T, ok: false }
|
|
292
331
|
}
|
|
293
332
|
|
|
294
333
|
/**
|
|
@@ -715,3 +754,60 @@ export const makeChannel = <T>(
|
|
|
715
754
|
): Channel<T> => {
|
|
716
755
|
return new BufferedChannel<T>(bufferSize, zeroValue)
|
|
717
756
|
}
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* DisposableStack manages synchronous disposable resources, mimicking Go's defer behavior.
|
|
760
|
+
* Functions added via `defer` are executed in LIFO order when the stack is disposed.
|
|
761
|
+
* Implements the `Disposable` interface for use with `using` declarations.
|
|
762
|
+
*/
|
|
763
|
+
export class DisposableStack implements Disposable {
|
|
764
|
+
#stack: (() => void)[] = [];
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Adds a function to be executed when the stack is disposed.
|
|
768
|
+
* @param fn The function to defer.
|
|
769
|
+
*/
|
|
770
|
+
defer(fn: () => void): void { this.#stack.push(fn); }
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* Disposes of the resources in the stack by executing the deferred functions
|
|
774
|
+
* in Last-In, First-Out (LIFO) order.
|
|
775
|
+
* If a deferred function throws an error, disposal stops, and the error is rethrown,
|
|
776
|
+
* similar to Go's panic behavior during defer execution.
|
|
777
|
+
*/
|
|
778
|
+
[Symbol.dispose](): void {
|
|
779
|
+
// Emulate Go: if a deferred throws, stop and rethrow
|
|
780
|
+
while (this.#stack.length) {
|
|
781
|
+
const fn = this.#stack.pop()!;
|
|
782
|
+
fn();
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* AsyncDisposableStack manages asynchronous disposable resources, mimicking Go's defer behavior.
|
|
789
|
+
* Functions added via `defer` are executed sequentially in LIFO order when the stack is disposed.
|
|
790
|
+
* Implements the `AsyncDisposable` interface for use with `await using` declarations.
|
|
791
|
+
*/
|
|
792
|
+
export class AsyncDisposableStack implements AsyncDisposable {
|
|
793
|
+
#stack: (() => Promise<void> | void)[] = [];
|
|
794
|
+
|
|
795
|
+
/**
|
|
796
|
+
* Adds a synchronous or asynchronous function to be executed when the stack is disposed.
|
|
797
|
+
* @param fn The function to defer. Can return void or a Promise<void>.
|
|
798
|
+
*/
|
|
799
|
+
defer(fn: () => Promise<void> | void): void {
|
|
800
|
+
this.#stack.push(fn);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* Asynchronously disposes of the resources in the stack by executing the deferred functions
|
|
805
|
+
* sequentially in Last-In, First-Out (LIFO) order. It awaits each function if it returns a promise.
|
|
806
|
+
*/
|
|
807
|
+
async [Symbol.asyncDispose](): Promise<void> {
|
|
808
|
+
// Execute in LIFO order, awaiting each potentially async function
|
|
809
|
+
for (let i = this.#stack.length - 1; i >= 0; --i) {
|
|
810
|
+
await this.#stack[i]();
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
@@ -12,7 +12,7 @@ import (
|
|
|
12
12
|
var (
|
|
13
13
|
cliCompiler *compiler.Compiler
|
|
14
14
|
cliCompilerConfig compiler.Config
|
|
15
|
-
cliCompilerPkg
|
|
15
|
+
cliCompilerPkg cli.StringSlice
|
|
16
16
|
)
|
|
17
17
|
|
|
18
18
|
// CompileCommands are commands related to compiling code.
|
|
@@ -29,9 +29,10 @@ var CompileCommands = []*cli.Command{{
|
|
|
29
29
|
return
|
|
30
30
|
},
|
|
31
31
|
Flags: []cli.Flag{
|
|
32
|
-
&cli.
|
|
32
|
+
&cli.StringSliceFlag{
|
|
33
33
|
Name: "package",
|
|
34
|
-
Usage: "the package to compile",
|
|
34
|
+
Usage: "the package(s) to compile",
|
|
35
|
+
Aliases: []string{"p", "packages"},
|
|
35
36
|
Destination: &cliCompilerPkg,
|
|
36
37
|
},
|
|
37
38
|
&cli.StringFlag{
|
|
@@ -51,9 +52,10 @@ var CompileCommands = []*cli.Command{{
|
|
|
51
52
|
|
|
52
53
|
// compilePackage tries to compile the package.
|
|
53
54
|
func compilePackage(c *cli.Context) error {
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
pkgs := cliCompilerPkg.Value()
|
|
56
|
+
if len(pkgs) == 0 {
|
|
57
|
+
return errors.New("package(s) must be specified")
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
return cliCompiler.CompilePackages(context.Background(),
|
|
60
|
+
return cliCompiler.CompilePackages(context.Background(), pkgs...)
|
|
59
61
|
}
|
package/compiler/compile.go
CHANGED
|
@@ -12,11 +12,13 @@ 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
|
|
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
|
|
20
22
|
|
|
21
23
|
tempVarCounter int // Counter for generating unique temporary variable names
|
|
22
24
|
}
|
|
@@ -68,15 +70,30 @@ func (c *GoToTSCompiler) WriteGoType(typ types.Type) {
|
|
|
68
70
|
}
|
|
69
71
|
}
|
|
70
72
|
|
|
73
|
+
// scanForDefer checks if a block contains any defer statements (recursively).
|
|
74
|
+
func (c *GoToTSCompiler) scanForDefer(block *ast.BlockStmt) bool {
|
|
75
|
+
hasDefer := false
|
|
76
|
+
ast.Inspect(block, func(n ast.Node) bool {
|
|
77
|
+
if _, ok := n.(*ast.DeferStmt); ok {
|
|
78
|
+
hasDefer = true
|
|
79
|
+
return false // Stop traversal
|
|
80
|
+
}
|
|
81
|
+
return true // Continue traversal
|
|
82
|
+
})
|
|
83
|
+
return hasDefer
|
|
84
|
+
}
|
|
85
|
+
|
|
71
86
|
// NewGoToTSCompiler builds a new GoToTSCompiler
|
|
72
87
|
func NewGoToTSCompiler(tsw *TSCodeWriter, pkg *packages.Package, cmap ast.CommentMap) *GoToTSCompiler {
|
|
73
88
|
return &GoToTSCompiler{
|
|
74
|
-
tsw:
|
|
75
|
-
imports:
|
|
76
|
-
pkg:
|
|
77
|
-
cmap:
|
|
78
|
-
asyncFuncs:
|
|
79
|
-
|
|
89
|
+
tsw: tsw,
|
|
90
|
+
imports: make(map[string]*fileImport),
|
|
91
|
+
pkg: pkg,
|
|
92
|
+
cmap: cmap,
|
|
93
|
+
asyncFuncs: make(map[string]bool),
|
|
94
|
+
nextBlockNeedsDefer: false,
|
|
95
|
+
inAsyncFunction: false,
|
|
96
|
+
tempVarCounter: 0, // Initialize counter
|
|
80
97
|
}
|
|
81
98
|
}
|
|
82
99
|
|
|
@@ -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
|
}
|
package/compiler/compile_expr.go
CHANGED
|
@@ -108,6 +108,43 @@ func (c *GoToTSCompiler) WriteValueExpr(a ast.Expr) error {
|
|
|
108
108
|
}
|
|
109
109
|
c.tsw.WriteLiterally("]")
|
|
110
110
|
return nil
|
|
111
|
+
case *ast.SliceExpr:
|
|
112
|
+
// Translate Go slice expression to goscript.slice(x, low, high, max)
|
|
113
|
+
c.tsw.WriteLiterally("goscript.slice(")
|
|
114
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
115
|
+
return err
|
|
116
|
+
}
|
|
117
|
+
// low argument
|
|
118
|
+
c.tsw.WriteLiterally(", ")
|
|
119
|
+
if exp.Low != nil {
|
|
120
|
+
if err := c.WriteValueExpr(exp.Low); err != nil {
|
|
121
|
+
return err
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
c.tsw.WriteLiterally("undefined")
|
|
125
|
+
}
|
|
126
|
+
// high argument
|
|
127
|
+
c.tsw.WriteLiterally(", ")
|
|
128
|
+
if exp.High != nil {
|
|
129
|
+
if err := c.WriteValueExpr(exp.High); err != nil {
|
|
130
|
+
return err
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
c.tsw.WriteLiterally("undefined")
|
|
134
|
+
}
|
|
135
|
+
// max argument (only for full slice expressions)
|
|
136
|
+
if exp.Slice3 {
|
|
137
|
+
c.tsw.WriteLiterally(", ")
|
|
138
|
+
if exp.Max != nil {
|
|
139
|
+
if err := c.WriteValueExpr(exp.Max); err != nil {
|
|
140
|
+
return err
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
c.tsw.WriteLiterally("undefined")
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
c.tsw.WriteLiterally(")")
|
|
147
|
+
return nil
|
|
111
148
|
case *ast.ParenExpr:
|
|
112
149
|
// Translate (X) to (X)
|
|
113
150
|
c.tsw.WriteLiterally("(")
|
|
@@ -433,7 +470,20 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
433
470
|
|
|
434
471
|
// Handle slice creation
|
|
435
472
|
if _, ok := exp.Args[0].(*ast.ArrayType); ok {
|
|
436
|
-
|
|
473
|
+
// Get the slice type information
|
|
474
|
+
sliceType := c.pkg.TypesInfo.TypeOf(exp.Args[0])
|
|
475
|
+
if sliceType == nil {
|
|
476
|
+
return errors.New("could not get type information for slice in make call")
|
|
477
|
+
}
|
|
478
|
+
goElemType, ok := sliceType.Underlying().(*gtypes.Slice)
|
|
479
|
+
if !ok {
|
|
480
|
+
return errors.New("expected slice type for make call")
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
c.tsw.WriteLiterally("goscript.makeSlice<")
|
|
484
|
+
c.WriteGoType(goElemType.Elem()) // Write the element type
|
|
485
|
+
c.tsw.WriteLiterally(">(")
|
|
486
|
+
|
|
437
487
|
if len(exp.Args) >= 2 {
|
|
438
488
|
if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
|
|
439
489
|
return err
|
|
@@ -885,11 +935,18 @@ func (c *GoToTSCompiler) WriteFuncLitValue(exp *ast.FuncLit) error {
|
|
|
885
935
|
}
|
|
886
936
|
|
|
887
937
|
c.tsw.WriteLiterally(" => ")
|
|
938
|
+
|
|
939
|
+
// Save previous async state and set current state based on isAsync
|
|
940
|
+
previousAsyncState := c.inAsyncFunction
|
|
941
|
+
c.inAsyncFunction = isAsync
|
|
888
942
|
|
|
889
943
|
// Write function body
|
|
890
944
|
if err := c.WriteStmt(exp.Body); err != nil {
|
|
945
|
+
c.inAsyncFunction = previousAsyncState // Restore state before returning error
|
|
891
946
|
return fmt.Errorf("failed to write function literal body: %w", err)
|
|
892
947
|
}
|
|
893
|
-
|
|
948
|
+
|
|
949
|
+
// Restore previous async state
|
|
950
|
+
c.inAsyncFunction = previousAsyncState
|
|
894
951
|
return nil
|
|
895
952
|
}
|