goscript 0.0.2

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.
Files changed (160) hide show
  1. package/.aider-prompt +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +427 -0
  4. package/builtin/builtin.ts +507 -0
  5. package/cmd/goscript/cmd_compile.go +59 -0
  6. package/cmd/goscript/main.go +23 -0
  7. package/compiler/compile.go +183 -0
  8. package/compiler/compile_comment.go +41 -0
  9. package/compiler/compile_decls.go +72 -0
  10. package/compiler/compile_expr.go +831 -0
  11. package/compiler/compile_field.go +89 -0
  12. package/compiler/compile_spec.go +256 -0
  13. package/compiler/compile_stmt.go +1509 -0
  14. package/compiler/compiler.go +81 -0
  15. package/compiler/config.go +32 -0
  16. package/compiler/context.go +9 -0
  17. package/compiler/file_compiler.go +80 -0
  18. package/compiler/output_path.go +31 -0
  19. package/compiler/pkg_compiler.go +73 -0
  20. package/compiler/writer.go +90 -0
  21. package/compliance/COMPLIANCE.md +133 -0
  22. package/compliance/compliance.go +313 -0
  23. package/compliance/compliance_test.go +57 -0
  24. package/compliance/tests/array_literal/array_literal.go +15 -0
  25. package/compliance/tests/array_literal/array_literal.gs.ts +19 -0
  26. package/compliance/tests/array_literal/expected.log +3 -0
  27. package/compliance/tests/async_basic/async_basic.go +26 -0
  28. package/compliance/tests/async_basic/async_basic.gs.ts +30 -0
  29. package/compliance/tests/async_basic/expected.log +1 -0
  30. package/compliance/tests/basic_arithmetic/basic_arithmetic.go +15 -0
  31. package/compliance/tests/basic_arithmetic/basic_arithmetic.gs.ts +19 -0
  32. package/compliance/tests/basic_arithmetic/expected.log +5 -0
  33. package/compliance/tests/boolean_logic/boolean_logic.go +13 -0
  34. package/compliance/tests/boolean_logic/boolean_logic.gs.ts +17 -0
  35. package/compliance/tests/boolean_logic/expected.log +3 -0
  36. package/compliance/tests/channel_basic/channel_basic.go +12 -0
  37. package/compliance/tests/channel_basic/channel_basic.gs.ts +18 -0
  38. package/compliance/tests/channel_basic/expected.log +1 -0
  39. package/compliance/tests/composite_literal_assignment/composite_literal_assignment.go +20 -0
  40. package/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.ts +27 -0
  41. package/compliance/tests/composite_literal_assignment/expected.log +2 -0
  42. package/compliance/tests/constants/constants.go +18 -0
  43. package/compliance/tests/constants/constants.gs.ts +22 -0
  44. package/compliance/tests/constants/expected.log +3 -0
  45. package/compliance/tests/copy_independence/copy_independence.go +29 -0
  46. package/compliance/tests/copy_independence/copy_independence.gs.ts +36 -0
  47. package/compliance/tests/copy_independence/expected.log +4 -0
  48. package/compliance/tests/float64/expected.log +6 -0
  49. package/compliance/tests/float64/float64.go +28 -0
  50. package/compliance/tests/float64/float64.gs.ts +32 -0
  51. package/compliance/tests/for_loop_basic/expected.log +5 -0
  52. package/compliance/tests/for_loop_basic/for_loop_basic.go +9 -0
  53. package/compliance/tests/for_loop_basic/for_loop_basic.gs.ts +13 -0
  54. package/compliance/tests/for_loop_condition_only/expected.log +5 -0
  55. package/compliance/tests/for_loop_condition_only/main.go +9 -0
  56. package/compliance/tests/for_loop_condition_only/main.gs.ts +13 -0
  57. package/compliance/tests/for_range/expected.log +9 -0
  58. package/compliance/tests/for_range/for_range.go +26 -0
  59. package/compliance/tests/for_range/for_range.gs.ts +45 -0
  60. package/compliance/tests/for_range_index_use/expected.log +6 -0
  61. package/compliance/tests/for_range_index_use/for_range_index_use.go +11 -0
  62. package/compliance/tests/for_range_index_use/for_range_index_use.gs.ts +18 -0
  63. package/compliance/tests/func_literal/expected.log +1 -0
  64. package/compliance/tests/func_literal/func_literal.go +10 -0
  65. package/compliance/tests/func_literal/func_literal.gs.ts +15 -0
  66. package/compliance/tests/function_call_result_assignment/expected.log +2 -0
  67. package/compliance/tests/function_call_result_assignment/function_call_result_assignment.go +24 -0
  68. package/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.ts +31 -0
  69. package/compliance/tests/if_statement/expected.log +1 -0
  70. package/compliance/tests/if_statement/if_statement.go +11 -0
  71. package/compliance/tests/if_statement/if_statement.gs.ts +15 -0
  72. package/compliance/tests/interface_to_interface_type_assertion/expected.log +1 -0
  73. package/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.go +30 -0
  74. package/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.ts +41 -0
  75. package/compliance/tests/interface_type_assertion/expected.log +1 -0
  76. package/compliance/tests/interface_type_assertion/interface_type_assertion.go +26 -0
  77. package/compliance/tests/interface_type_assertion/interface_type_assertion.gs.ts +36 -0
  78. package/compliance/tests/map_support/expected.log +13 -0
  79. package/compliance/tests/map_support/map_support.go +89 -0
  80. package/compliance/tests/map_support/map_support.gs.ts +102 -0
  81. package/compliance/tests/method_call_on_pointer_receiver/expected.log +1 -0
  82. package/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.go +19 -0
  83. package/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.ts +27 -0
  84. package/compliance/tests/method_call_on_pointer_via_value/expected.log +1 -0
  85. package/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.go +29 -0
  86. package/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.ts +38 -0
  87. package/compliance/tests/method_call_on_value_receiver/expected.log +1 -0
  88. package/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.go +16 -0
  89. package/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.ts +24 -0
  90. package/compliance/tests/method_call_on_value_via_pointer/expected.log +2 -0
  91. package/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.go +30 -0
  92. package/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.ts +38 -0
  93. package/compliance/tests/multiple_return_values/expected.log +6 -0
  94. package/compliance/tests/multiple_return_values/multiple_return_values.go +19 -0
  95. package/compliance/tests/multiple_return_values/multiple_return_values.gs.ts +23 -0
  96. package/compliance/tests/pointer_assignment_no_copy/expected.log +2 -0
  97. package/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.go +28 -0
  98. package/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.ts +35 -0
  99. package/compliance/tests/pointer_composite_literal_assignment/expected.log +3 -0
  100. package/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.go +23 -0
  101. package/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.ts +30 -0
  102. package/compliance/tests/pointer_deref_multiassign/expected.log +0 -0
  103. package/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.go +17 -0
  104. package/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.ts +27 -0
  105. package/compliance/tests/pointer_initialization/expected.log +1 -0
  106. package/compliance/tests/pointer_initialization/pointer_initialization.go +16 -0
  107. package/compliance/tests/pointer_initialization/pointer_initialization.gs.ts +22 -0
  108. package/compliance/tests/select_receive_on_closed_channel_no_default/expected.log +1 -0
  109. package/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.go +15 -0
  110. package/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.ts +31 -0
  111. package/compliance/tests/select_send_on_full_buffered_channel_with_default/expected.log +1 -0
  112. package/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.go +13 -0
  113. package/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.gs.ts +35 -0
  114. package/compliance/tests/select_statement/expected.log +9 -0
  115. package/compliance/tests/select_statement/select_statement.go +109 -0
  116. package/compliance/tests/select_statement/select_statement.gs.ts +239 -0
  117. package/compliance/tests/simple/expected.log +1 -0
  118. package/compliance/tests/simple/simple.go +5 -0
  119. package/compliance/tests/simple/simple.gs.ts +9 -0
  120. package/compliance/tests/simple_deref_assignment/expected.log +2 -0
  121. package/compliance/tests/simple_deref_assignment/simple_deref_assignment.go +19 -0
  122. package/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.ts +26 -0
  123. package/compliance/tests/slices/expected.log +7 -0
  124. package/compliance/tests/slices/slices.go +22 -0
  125. package/compliance/tests/slices/slices.gs.ts +26 -0
  126. package/compliance/tests/string_rune_conversion/expected.log +3 -0
  127. package/compliance/tests/string_rune_conversion/string_rune_conversion.go +16 -0
  128. package/compliance/tests/string_rune_conversion/string_rune_conversion.gs.ts +22 -0
  129. package/compliance/tests/struct_field_access/expected.log +2 -0
  130. package/compliance/tests/struct_field_access/struct_field_access.go +13 -0
  131. package/compliance/tests/struct_field_access/struct_field_access.gs.ts +20 -0
  132. package/compliance/tests/struct_value_init_clone/expected.log +5 -0
  133. package/compliance/tests/struct_value_init_clone/struct_value_init_clone.go +28 -0
  134. package/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.ts +35 -0
  135. package/compliance/tests/switch_statement/expected.log +14 -0
  136. package/compliance/tests/switch_statement/switch_statement.go +59 -0
  137. package/compliance/tests/switch_statement/switch_statement.gs.ts +85 -0
  138. package/compliance/tests/value_type_copy_behavior/expected.log +3 -0
  139. package/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.go +25 -0
  140. package/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.ts +34 -0
  141. package/design/DESIGN.md +599 -0
  142. package/example/simple/build.bash +10 -0
  143. package/example/simple/go.mod +23 -0
  144. package/example/simple/go.sum +39 -0
  145. package/example/simple/main.go +138 -0
  146. package/example/simple/main.gs.ts +133 -0
  147. package/example/simple/main.ts +3 -0
  148. package/example/simple/main_test.go +59 -0
  149. package/example/simple/main_tools.go +5 -0
  150. package/example/simple/package.json +7 -0
  151. package/example/simple/run.bash +6 -0
  152. package/example/simple/tsconfig.json +28 -0
  153. package/example/simple/yarn.lock +8 -0
  154. package/go.mod +22 -0
  155. package/go.sum +39 -0
  156. package/output/output.go +10 -0
  157. package/package.json +14 -0
  158. package/tsconfig.json +10 -0
  159. package/types/tokens.go +65 -0
  160. package/types/types.go +46 -0
@@ -0,0 +1,507 @@
1
+ /**
2
+ * Creates a new slice (TypeScript array) with the specified length and capacity.
3
+ * @param elementType The element type of the slice (for type hinting).
4
+ * @param len The length of the slice.
5
+ * @param cap The capacity of the slice (optional).
6
+ * @returns A new TypeScript array representing the slice.
7
+ */
8
+ export const makeSlice = <T>(elementType: any, len: number, cap?: number): Array<T> & { __capacity?: number } => {
9
+ const slice = new Array<T>(len) as Array<T> & { __capacity?: number };
10
+ slice.__capacity = cap !== undefined ? cap : len;
11
+ return slice;
12
+ };
13
+
14
+ /**
15
+ * Creates a new map (TypeScript Map).
16
+ * @param keyType The key type of the map (for type hinting).
17
+ * @param valueType The value type of the map (for type hinting).
18
+ * @returns A new TypeScript Map.
19
+ */
20
+ export const makeMap = <K, V>(keyType: any, valueType: any): Map<K, V> => {
21
+ return new Map<K, V>();
22
+ };
23
+
24
+ /**
25
+ * Returns the length of a collection (string, array, or map).
26
+ * @param collection The collection to get the length of.
27
+ * @returns The length of the collection.
28
+ */
29
+ export const len = <T>(collection: string | Array<T> | Map<any, any>): number => {
30
+ if (typeof collection === 'string' || Array.isArray(collection)) {
31
+ return collection.length;
32
+ } else if (collection instanceof Map) {
33
+ return collection.size;
34
+ }
35
+ return 0; // Default fallback
36
+ };
37
+
38
+ /**
39
+ * Returns the capacity of a slice (TypeScript array).
40
+ * @param slice The slice (TypeScript array).
41
+ * @returns The capacity of the slice.
42
+ */
43
+ export const cap = <T>(slice: Array<T> & { __capacity?: number }): number => {
44
+ return slice.__capacity !== undefined ? slice.__capacity : slice.length;
45
+ };
46
+
47
+ /**
48
+ * Converts a string to an array of Unicode code points (runes).
49
+ * @param str The input string.
50
+ * @returns An array of numbers representing the Unicode code points.
51
+ */
52
+ export const stringToRunes = (str: string): number[] => {
53
+ return Array.from(str).map(c => c.codePointAt(0) || 0);
54
+ };
55
+
56
+ /**
57
+ * Gets a value from a map, with a default value if the key doesn't exist.
58
+ * @param map The map to get from.
59
+ * @param key The key to get.
60
+ * @param defaultValue The default value to return if the key doesn't exist (defaults to 0).
61
+ * @returns The value for the key, or the default value if the key doesn't exist.
62
+ */
63
+ export const mapGet = <K, V>(map: Map<K, V>, key: K, defaultValue: V | null = null): V | null => {
64
+ return map.has(key) ? map.get(key)! : defaultValue;
65
+ };
66
+
67
+ /**
68
+ * Sets a value in a map.
69
+ * @param map The map to set in.
70
+ * @param key The key to set.
71
+ * @param value The value to set.
72
+ */
73
+ export const mapSet = <K, V>(map: Map<K, V>, key: K, value: V): void => {
74
+ map.set(key, value);
75
+ };
76
+
77
+ /**
78
+ * Deletes a key from a map.
79
+ * @param map The map to delete from.
80
+ * @param key The key to delete.
81
+ */
82
+ export const deleteMapEntry = <K, V>(map: Map<K, V>, key: K): void => {
83
+ map.delete(key);
84
+ };
85
+
86
+ /**
87
+ * Checks if a key exists in a map.
88
+ * @param map The map to check in.
89
+ * @param key The key to check.
90
+ * @returns True if the key exists, false otherwise.
91
+ */
92
+ export const mapHas = <K, V>(map: Map<K, V>, key: K): boolean => {
93
+ return map.has(key);
94
+ };
95
+ /**
96
+ * Appends elements to a slice (TypeScript array).
97
+ * Note: In Go, append can return a new slice if the underlying array is reallocated.
98
+ * This helper emulates that by returning the modified array.
99
+ * @param slice The slice (TypeScript array) to append to.
100
+ * @param elements The elements to append.
101
+ * @returns The modified slice (TypeScript array).
102
+ */
103
+ export const append = <T>(slice: Array<T>, ...elements: T[]): Array<T> => {
104
+ slice.push(...elements);
105
+ return slice;
106
+ };
107
+
108
+ /**
109
+ * Represents the result of a channel receive operation with 'ok' value
110
+ */
111
+ export interface ChannelReceiveResult<T> {
112
+ value: T; // Should be T | ZeroValue<T>
113
+ ok: boolean;
114
+ }
115
+
116
+ /**
117
+ * Represents a result from a select operation
118
+ */
119
+ export interface SelectResult<T> {
120
+ value: T; // Should be T | ZeroValue<T>
121
+ ok: boolean;
122
+ id: number;
123
+ }
124
+
125
+ /**
126
+ * Represents a Go channel in TypeScript.
127
+ * Supports asynchronous sending and receiving of values.
128
+ */
129
+ export interface Channel<T> {
130
+ /**
131
+ * Sends a value to the channel.
132
+ * Returns a promise that resolves when the value is accepted by the channel.
133
+ * @param value The value to send.
134
+ */
135
+ send(value: T): Promise<void>;
136
+
137
+ /**
138
+ * Receives a value from the channel.
139
+ * Returns a promise that resolves with the received value.
140
+ * If the channel is closed, it throws an error.
141
+ */
142
+ receive(): Promise<T>;
143
+
144
+ /**
145
+ * Receives a value from the channel along with a boolean indicating
146
+ * whether the channel is still open.
147
+ * Returns a promise that resolves with {value, ok}.
148
+ * - If channel is open and has data: {value: <data>, ok: true}
149
+ * - If channel is closed and empty: {value: <zero value>, ok: false}
150
+ * - If channel is closed but has remaining buffered data: {value: <data>, ok: true}
151
+ */
152
+ receiveWithOk(): Promise<ChannelReceiveResult<T>>;
153
+
154
+ /**
155
+ * Closes the channel.
156
+ * No more values can be sent to a closed channel.
157
+ * Receive operations on a closed channel return the zero value and ok=false.
158
+ */
159
+ close(): void;
160
+
161
+ /**
162
+ * Used in select statements to create a receive operation promise.
163
+ * @param id An identifier for this case in the select statement
164
+ * @returns Promise that resolves when this case is selected
165
+ */
166
+ selectReceive(id: number): Promise<SelectResult<T>>;
167
+
168
+ /**
169
+ * Used in select statements to create a send operation promise.
170
+ * @param value The value to send
171
+ * @param id An identifier for this case in the select statement
172
+ * @returns Promise that resolves when this case is selected
173
+ */
174
+ selectSend(value: T, id: number): Promise<SelectResult<boolean>>;
175
+
176
+ /**
177
+ * Checks if the channel has data ready to be received without blocking.
178
+ * Used for non-blocking select operations.
179
+ */
180
+ canReceiveNonBlocking(): boolean;
181
+
182
+ /**
183
+ * Checks if the channel can accept a send operation without blocking.
184
+ * Used for non-blocking select operations.
185
+ */
186
+ canSendNonBlocking(): boolean;
187
+ }
188
+
189
+ // A simple implementation of buffered channels
190
+ class BufferedChannel<T> implements Channel<T> {
191
+ private buffer: T[] = [];
192
+ private closed: boolean = false;
193
+ private capacity: number;
194
+ private senders: Array<(value: boolean) => void> = []; // Resolvers for blocked senders
195
+ private receivers: Array<(value: T) => void> = []; // Resolvers for blocked receivers
196
+ private receiversWithOk: Array<(result: ChannelReceiveResult<T>) => void> = []; // For receive with ok
197
+ private zeroValue: T; // Store the zero value for the element type
198
+
199
+ constructor(capacity: number, zeroValue: T) {
200
+ this.capacity = capacity;
201
+ this.zeroValue = zeroValue;
202
+ }
203
+
204
+ async send(value: T): Promise<void> {
205
+ if (this.closed) {
206
+ throw new Error("send on closed channel");
207
+ }
208
+
209
+ // If there are waiting receivers, directly pass the value
210
+ if (this.receivers.length > 0) {
211
+ const receiver = this.receivers.shift()!;
212
+ receiver(value);
213
+ return;
214
+ }
215
+
216
+ // If there are waiting receivers with ok, directly pass the value and ok=true
217
+ if (this.receiversWithOk.length > 0) {
218
+ const receiver = this.receiversWithOk.shift()!;
219
+ receiver({ value, ok: true });
220
+ return;
221
+ }
222
+
223
+ // If buffer is not full, add to buffer
224
+ if (this.buffer.length < this.capacity) {
225
+ this.buffer.push(value);
226
+ return;
227
+ }
228
+
229
+ // Buffer is full, block the sender
230
+ return new Promise<void>((resolve, reject) => {
231
+ this.senders.push((success: boolean) => {
232
+ if (success) {
233
+ this.buffer.push(value);
234
+ resolve();
235
+ } else {
236
+ reject(new Error("send on closed channel"));
237
+ }
238
+ });
239
+ });
240
+ }
241
+
242
+ async receive(): Promise<T> {
243
+ // If buffer has values, return from buffer
244
+ if (this.buffer.length > 0) {
245
+ const value = this.buffer.shift()!;
246
+
247
+ // If there are waiting senders, unblock one
248
+ if (this.senders.length > 0) {
249
+ const sender = this.senders.shift()!;
250
+ sender(true); // Unblock with success
251
+ }
252
+
253
+ return value;
254
+ }
255
+
256
+ // If channel is closed and buffer is empty, throw error (receive without ok)
257
+ if (this.closed) {
258
+ throw new Error("receive on closed channel");
259
+ }
260
+
261
+ // Buffer is empty, block the receiver
262
+ return new Promise<T>((resolve) => {
263
+ this.receivers.push(resolve);
264
+ });
265
+ }
266
+
267
+ async receiveWithOk(): Promise<ChannelReceiveResult<T>> {
268
+ // If buffer has values, return from buffer with ok=true
269
+ if (this.buffer.length > 0) {
270
+ const value = this.buffer.shift()!;
271
+
272
+ // If there are waiting senders, unblock one
273
+ if (this.senders.length > 0) {
274
+ const sender = this.senders.shift()!;
275
+ sender(true); // Unblock with success
276
+ }
277
+
278
+ return { value, ok: true };
279
+ }
280
+
281
+ // If channel is closed and buffer is empty, return zero value with ok=false
282
+ if (this.closed) {
283
+ return { value: this.zeroValue, ok: false }; // Return zeroValue
284
+ }
285
+
286
+ // Buffer is empty, block the receiver with ok
287
+ return new Promise<ChannelReceiveResult<T>>((resolve) => {
288
+ this.receiversWithOk.push(resolve);
289
+ });
290
+ }
291
+
292
+ async selectReceive(id: number): Promise<SelectResult<T>> {
293
+ // If buffer has values, return from buffer with ok=true
294
+ if (this.buffer.length > 0) {
295
+ const value = this.buffer.shift()!;
296
+
297
+ // If there are waiting senders, unblock one
298
+ if (this.senders.length > 0) {
299
+ const sender = this.senders.shift()!;
300
+ sender(true); // Unblock with success
301
+ }
302
+
303
+ return { value, ok: true, id };
304
+ }
305
+
306
+ // If channel is closed and buffer is empty, return zero value with ok=false
307
+ if (this.closed) {
308
+ return { value: this.zeroValue, ok: false, id }; // Return zeroValue
309
+ }
310
+
311
+ // Buffer is empty, return a promise that will be resolved when a value is available
312
+ return new Promise<SelectResult<T>>((resolve) => {
313
+ this.receiversWithOk.push((result) => {
314
+ resolve({ ...result, id });
315
+ });
316
+ });
317
+ }
318
+
319
+ async selectSend(value: T, id: number): Promise<SelectResult<boolean>> {
320
+ if (this.closed) {
321
+ return { value: false, ok: false, id };
322
+ }
323
+
324
+ // If there are waiting receivers, directly pass the value
325
+ if (this.receivers.length > 0) {
326
+ const receiver = this.receivers.shift()!;
327
+ receiver(value);
328
+ return { value: true, ok: true, id };
329
+ }
330
+
331
+ // If there are waiting receivers with ok, directly pass the value and ok=true
332
+ if (this.receiversWithOk.length > 0) {
333
+ const receiver = this.receiversWithOk.shift()!;
334
+ receiver({ value, ok: true });
335
+ return { value: true, ok: true, id };
336
+ }
337
+
338
+ // If buffer is not full, add to buffer
339
+ if (this.buffer.length < this.capacity) {
340
+ this.buffer.push(value);
341
+ return { value: true, ok: true, id };
342
+ }
343
+
344
+ // Buffer is full, return a promise that will be resolved when buffer space is available
345
+ return new Promise<SelectResult<boolean>>((resolve, reject) => {
346
+ this.senders.push((success: boolean) => {
347
+ if (success) {
348
+ this.buffer.push(value);
349
+ resolve({ value: true, ok: true, id });
350
+ } else {
351
+ resolve({ value: false, ok: false, id });
352
+ }
353
+ });
354
+ });
355
+ }
356
+
357
+ close(): void {
358
+ if (this.closed) {
359
+ throw new Error("close of closed channel");
360
+ }
361
+
362
+ this.closed = true;
363
+
364
+ // Unblock all waiting senders with failure
365
+ for (const sender of this.senders) {
366
+ sender(false);
367
+ }
368
+ this.senders = [];
369
+
370
+ // Unblock all waiting receivers with the zero value
371
+ for (const receiver of this.receivers) {
372
+ // Note: receive() without ok throws on closed channel, this unblocking should not happen in correct Go logic
373
+ // but for safety, we'll resolve with zero value if it somehow does.
374
+ receiver(this.zeroValue);
375
+ }
376
+ this.receivers = [];
377
+
378
+ // Unblock all waiting receivers with ok=false and zero value
379
+ for (const receiver of this.receiversWithOk) {
380
+ receiver({ value: this.zeroValue, ok: false });
381
+ }
382
+ this.receiversWithOk = [];
383
+ }
384
+
385
+ /**
386
+ * Checks if the channel has data ready to be received without blocking.
387
+ * Used for non-blocking select operations.
388
+ */
389
+ canReceiveNonBlocking(): boolean {
390
+ return this.buffer.length > 0 || this.closed;
391
+ }
392
+
393
+ /**
394
+ * Checks if the channel can accept a send operation without blocking.
395
+ * Used for non-blocking select operations.
396
+ */
397
+ canSendNonBlocking(): boolean {
398
+ return !this.closed && this.buffer.length < this.capacity;
399
+ }
400
+ }
401
+
402
+ /**
403
+ * Represents a case in a select statement.
404
+ */
405
+ export interface SelectCase<T> {
406
+ id: number;
407
+ isSend: boolean; // true for send, false for receive
408
+ channel: Channel<any>;
409
+ value?: any; // Value to send for send cases
410
+ // Optional handlers for when this case is selected
411
+ onSelected?: (result: SelectResult<T>) => Promise<void>;
412
+ }
413
+
414
+
415
+ /**
416
+ * Helper for 'select' statements. Takes an array of select cases
417
+ * and resolves when one of them completes, following Go's select rules.
418
+ *
419
+ * @param cases Array of SelectCase objects
420
+ * @param hasDefault Whether there is a default case
421
+ * @returns A promise that resolves with the result of the selected case
422
+ */
423
+ export async function selectStatement<T>(
424
+ cases: SelectCase<T>[],
425
+ hasDefault: boolean = false
426
+ ): Promise<void> {
427
+ if (cases.length === 0 && !hasDefault) {
428
+ // Go spec: If there are no cases, the select statement blocks forever.
429
+ // Emulate blocking forever with a promise that never resolves.
430
+ return new Promise<void>(() => {}); // Promise never resolves
431
+ }
432
+
433
+ // 1. Check for ready (non-blocking) operations
434
+ const readyCases: SelectCase<T>[] = [];
435
+ for (const caseObj of cases) {
436
+ if (caseObj.id === -1) { // Skip default case in this check
437
+ continue
438
+ }
439
+ if (caseObj.isSend) {
440
+ if (caseObj.channel.canSendNonBlocking()) {
441
+ readyCases.push(caseObj);
442
+ }
443
+ } else {
444
+ if (caseObj.channel.canReceiveNonBlocking()) {
445
+ readyCases.push(caseObj);
446
+ }
447
+ }
448
+ }
449
+
450
+ if (readyCases.length > 0) {
451
+ // If one or more cases are ready, choose one pseudo-randomly
452
+ const selectedCase = readyCases[Math.floor(Math.random() * readyCases.length)];
453
+
454
+ // Execute the selected operation and its onSelected handler
455
+ if (selectedCase.isSend) {
456
+ const result = await selectedCase.channel.selectSend(selectedCase.value, selectedCase.id);
457
+ if (selectedCase.onSelected) {
458
+ await selectedCase.onSelected(result as SelectResult<T>); // Await the handler
459
+ }
460
+ } else {
461
+ const result = await selectedCase.channel.selectReceive(selectedCase.id);
462
+ if (selectedCase.onSelected) {
463
+ await selectedCase.onSelected(result); // Await the handler
464
+ }
465
+ }
466
+ return; // Return after executing a ready case
467
+ }
468
+
469
+ // 2. If no operations are ready and there's a default case, select default
470
+ if (hasDefault) {
471
+ // Find the default case (it will have id -1)
472
+ const defaultCase = cases.find(c => c.id === -1);
473
+ if (defaultCase && defaultCase.onSelected) {
474
+ // Execute the onSelected handler for the default case
475
+ await defaultCase.onSelected({ value: undefined, ok: false, id: -1 } as SelectResult<T>); // Await the handler
476
+ }
477
+ return; // Return after executing the default case
478
+ }
479
+
480
+ // 3. If no operations are ready and no default case, block until one is ready
481
+ // Use Promise.race on the blocking promises
482
+ const blockingPromises = cases.filter(c => c.id !== -1).map(caseObj => { // Exclude default case
483
+ if (caseObj.isSend) {
484
+ return caseObj.channel.selectSend(caseObj.value, caseObj.id);
485
+ } else {
486
+ return caseObj.channel.selectReceive(caseObj.id);
487
+ }
488
+ });
489
+
490
+ const result = await Promise.race(blockingPromises);
491
+ // Execute onSelected handler for the selected case
492
+ const selectedCase = cases.find(c => c.id === result.id);
493
+ if (selectedCase && selectedCase.onSelected) {
494
+ await selectedCase.onSelected(result); // Await the handler
495
+ }
496
+ // No explicit return needed here, as the function will implicitly return after the await
497
+ }
498
+
499
+ /**
500
+ * Creates a new channel with the specified buffer size and zero value.
501
+ * @param bufferSize The size of the channel buffer. If 0, creates an unbuffered channel.
502
+ * @param zeroValue The zero value for the channel's element type.
503
+ * @returns A new channel instance.
504
+ */
505
+ export const makeChannel = <T>(bufferSize: number, zeroValue: T): Channel<T> => {
506
+ return new BufferedChannel<T>(bufferSize, zeroValue);
507
+ };
@@ -0,0 +1,59 @@
1
+ package main
2
+
3
+ import (
4
+ "context"
5
+
6
+ "github.com/aperturerobotics/cli"
7
+ "github.com/paralin/goscript/compiler"
8
+ "github.com/pkg/errors"
9
+ "github.com/sirupsen/logrus"
10
+ )
11
+
12
+ var (
13
+ cliCompiler *compiler.Compiler
14
+ cliCompilerConfig compiler.Config
15
+ cliCompilerPkg string
16
+ )
17
+
18
+ // CompileCommands are commands related to compiling code.
19
+ var CompileCommands = []*cli.Command{{
20
+ Name: "compile",
21
+ Category: "compile",
22
+ Usage: "compile a Go package to TypeScript",
23
+ Action: compilePackage,
24
+ Before: func(c *cli.Context) (err error) {
25
+ logger := logrus.New()
26
+ logger.SetLevel(logrus.DebugLevel)
27
+ le := logrus.NewEntry(logger)
28
+ cliCompiler, err = compiler.NewCompiler(&cliCompilerConfig, le, nil)
29
+ return
30
+ },
31
+ Flags: []cli.Flag{
32
+ &cli.StringFlag{
33
+ Name: "package",
34
+ Usage: "the package to compile",
35
+ Destination: &cliCompilerPkg,
36
+ },
37
+ &cli.StringFlag{
38
+ Name: "output",
39
+ Usage: "the output typescript path to use",
40
+ Destination: &cliCompilerConfig.OutputPathRoot,
41
+ Value: "./output",
42
+ },
43
+ &cli.StringFlag{
44
+ Name: "dir",
45
+ Usage: "the working directory to use for the compiler (default: current directory)",
46
+ Destination: &cliCompilerConfig.Dir,
47
+ Value: "",
48
+ },
49
+ },
50
+ }}
51
+
52
+ // compilePackage tries to compile the package.
53
+ func compilePackage(c *cli.Context) error {
54
+ if cliCompilerPkg == "" {
55
+ return errors.New("package must be specified")
56
+ }
57
+
58
+ return cliCompiler.CompilePackages(context.Background(), cliCompilerPkg)
59
+ }
@@ -0,0 +1,23 @@
1
+ package main
2
+
3
+ import (
4
+ "os"
5
+
6
+ "github.com/aperturerobotics/cli"
7
+ )
8
+
9
+ func main() {
10
+ app := cli.NewApp()
11
+
12
+ app.Authors = []*cli.Author{
13
+ {Name: "Christian Stewart", Email: "christian@aperture.us"},
14
+ }
15
+
16
+ app.Usage = "GoScript compiles Go to Typescript."
17
+ app.Commands = append(app.Commands, CompileCommands...)
18
+
19
+ if err := app.Run(os.Args); err != nil {
20
+ _, _ = os.Stderr.WriteString(err.Error() + "\n")
21
+ os.Exit(1)
22
+ }
23
+ }