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