goscript 0.0.22 → 0.0.24

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 (66) hide show
  1. package/README.md +1 -1
  2. package/cmd/goscript/cmd_compile.go +3 -3
  3. package/compiler/analysis.go +302 -182
  4. package/compiler/analysis_test.go +220 -0
  5. package/compiler/assignment.go +42 -43
  6. package/compiler/builtin_test.go +102 -0
  7. package/compiler/compiler.go +117 -29
  8. package/compiler/compiler_test.go +36 -8
  9. package/compiler/composite-lit.go +133 -53
  10. package/compiler/config.go +7 -3
  11. package/compiler/config_test.go +6 -33
  12. package/compiler/decl.go +36 -0
  13. package/compiler/expr-call.go +116 -60
  14. package/compiler/expr-selector.go +88 -43
  15. package/compiler/expr-star.go +57 -65
  16. package/compiler/expr-type.go +132 -5
  17. package/compiler/expr-value.go +8 -38
  18. package/compiler/expr.go +326 -30
  19. package/compiler/field.go +3 -3
  20. package/compiler/lit.go +34 -2
  21. package/compiler/primitive.go +19 -12
  22. package/compiler/spec-struct.go +140 -9
  23. package/compiler/spec-value.go +119 -41
  24. package/compiler/spec.go +21 -6
  25. package/compiler/stmt-assign.go +65 -3
  26. package/compiler/stmt-for.go +11 -0
  27. package/compiler/stmt-range.go +119 -11
  28. package/compiler/stmt-select.go +211 -0
  29. package/compiler/stmt-type-switch.go +147 -0
  30. package/compiler/stmt.go +175 -238
  31. package/compiler/type-assert.go +125 -379
  32. package/compiler/type.go +216 -129
  33. package/dist/gs/builtin/builtin.js +37 -0
  34. package/dist/gs/builtin/builtin.js.map +1 -0
  35. package/dist/gs/builtin/channel.js +471 -0
  36. package/dist/gs/builtin/channel.js.map +1 -0
  37. package/dist/gs/builtin/defer.js +54 -0
  38. package/dist/gs/builtin/defer.js.map +1 -0
  39. package/dist/gs/builtin/io.js +15 -0
  40. package/dist/gs/builtin/io.js.map +1 -0
  41. package/dist/gs/builtin/map.js +44 -0
  42. package/dist/gs/builtin/map.js.map +1 -0
  43. package/dist/gs/builtin/slice.js +799 -0
  44. package/dist/gs/builtin/slice.js.map +1 -0
  45. package/dist/gs/builtin/type.js +745 -0
  46. package/dist/gs/builtin/type.js.map +1 -0
  47. package/dist/gs/builtin/varRef.js +14 -0
  48. package/dist/gs/builtin/varRef.js.map +1 -0
  49. package/dist/gs/context/context.js +55 -0
  50. package/dist/gs/context/context.js.map +1 -0
  51. package/dist/gs/context/index.js +2 -0
  52. package/dist/gs/context/index.js.map +1 -0
  53. package/dist/gs/runtime/index.js +2 -0
  54. package/dist/gs/runtime/index.js.map +1 -0
  55. package/dist/gs/runtime/runtime.js +158 -0
  56. package/dist/gs/runtime/runtime.js.map +1 -0
  57. package/dist/gs/time/index.js +2 -0
  58. package/dist/gs/time/index.js.map +1 -0
  59. package/dist/gs/time/time.js +115 -0
  60. package/dist/gs/time/time.js.map +1 -0
  61. package/package.json +7 -6
  62. package/builtin/builtin.go +0 -11
  63. package/builtin/builtin.ts +0 -2379
  64. package/dist/builtin/builtin.d.ts +0 -513
  65. package/dist/builtin/builtin.js +0 -1686
  66. package/dist/builtin/builtin.js.map +0 -1
@@ -0,0 +1,745 @@
1
+ export { TypeKind };
2
+ /**
3
+ * Represents the kinds of Go types that can be registered at runtime.
4
+ */
5
+ var TypeKind;
6
+ (function (TypeKind) {
7
+ TypeKind["Basic"] = "basic";
8
+ TypeKind["Interface"] = "interface";
9
+ TypeKind["Struct"] = "struct";
10
+ TypeKind["Map"] = "map";
11
+ TypeKind["Slice"] = "slice";
12
+ TypeKind["Array"] = "array";
13
+ TypeKind["Pointer"] = "pointer";
14
+ TypeKind["Function"] = "function";
15
+ TypeKind["Channel"] = "channel";
16
+ })(TypeKind || (TypeKind = {}));
17
+ // Type guard functions for TypeInfo variants
18
+ export function isStructTypeInfo(info) {
19
+ return info.kind === TypeKind.Struct;
20
+ }
21
+ export function isInterfaceTypeInfo(info) {
22
+ return info.kind === TypeKind.Interface;
23
+ }
24
+ export function isBasicTypeInfo(info) {
25
+ return info.kind === TypeKind.Basic;
26
+ }
27
+ export function isMapTypeInfo(info) {
28
+ return info.kind === TypeKind.Map;
29
+ }
30
+ export function isSliceTypeInfo(info) {
31
+ return info.kind === TypeKind.Slice;
32
+ }
33
+ export function isArrayTypeInfo(info) {
34
+ return info.kind === TypeKind.Array;
35
+ }
36
+ export function isPointerTypeInfo(info) {
37
+ return info.kind === TypeKind.Pointer;
38
+ }
39
+ export function isFunctionTypeInfo(info) {
40
+ return info.kind === TypeKind.Function;
41
+ }
42
+ export function isChannelTypeInfo(info) {
43
+ return info.kind === TypeKind.Channel;
44
+ }
45
+ // Registry to store runtime type information
46
+ const typeRegistry = new Map();
47
+ /**
48
+ * Registers a struct type with the runtime type system.
49
+ *
50
+ * @param name The name of the type.
51
+ * @param zeroValue The zero value for the type.
52
+ * @param methods Array of method signatures for the struct.
53
+ * @param ctor Constructor for the struct.
54
+ * @param fields Record of field names and their types.
55
+ * @returns The struct type information object.
56
+ */
57
+ export const registerStructType = (name, zeroValue, methods, ctor, fields = {}) => {
58
+ const typeInfo = {
59
+ name,
60
+ kind: TypeKind.Struct,
61
+ zeroValue,
62
+ methods,
63
+ ctor,
64
+ fields,
65
+ };
66
+ typeRegistry.set(name, typeInfo);
67
+ return typeInfo;
68
+ };
69
+ /**
70
+ * Registers an interface type with the runtime type system.
71
+ *
72
+ * @param name The name of the type.
73
+ * @param zeroValue The zero value for the type (usually null).
74
+ * @param methods Array of method signatures for the interface.
75
+ * @returns The interface type information object.
76
+ */
77
+ export const registerInterfaceType = (name, zeroValue, methods) => {
78
+ const typeInfo = {
79
+ name,
80
+ kind: TypeKind.Interface,
81
+ zeroValue,
82
+ methods,
83
+ };
84
+ typeRegistry.set(name, typeInfo);
85
+ return typeInfo;
86
+ };
87
+ /**
88
+ * Normalizes a type info to a structured TypeInfo object.
89
+ *
90
+ * @param info The type info or name.
91
+ * @returns A normalized TypeInfo object.
92
+ */
93
+ function normalizeTypeInfo(info) {
94
+ if (typeof info === 'string') {
95
+ const typeInfo = typeRegistry.get(info);
96
+ if (typeInfo) {
97
+ return typeInfo;
98
+ }
99
+ return {
100
+ kind: TypeKind.Basic,
101
+ name: info,
102
+ };
103
+ }
104
+ return info;
105
+ }
106
+ function compareOptionalTypeInfo(type1, type2) {
107
+ if (type1 === undefined && type2 === undefined)
108
+ return true;
109
+ if (type1 === undefined || type2 === undefined)
110
+ return false;
111
+ // Assuming areTypeInfosIdentical will handle normalization if needed,
112
+ // but type1 and type2 here are expected to be direct fields from TypeInfo objects.
113
+ return areTypeInfosIdentical(type1, type2);
114
+ }
115
+ function areFuncParamOrResultArraysIdentical(arr1, arr2) {
116
+ if (arr1 === undefined && arr2 === undefined)
117
+ return true;
118
+ if (arr1 === undefined || arr2 === undefined)
119
+ return false;
120
+ if (arr1.length !== arr2.length)
121
+ return false;
122
+ for (let i = 0; i < arr1.length; i++) {
123
+ if (!areTypeInfosIdentical(arr1[i], arr2[i])) {
124
+ return false;
125
+ }
126
+ }
127
+ return true;
128
+ }
129
+ function areFuncSignaturesIdentical(func1, func2) {
130
+ if ((func1.isVariadic || false) !== (func2.isVariadic || false)) {
131
+ return false;
132
+ }
133
+ return (areFuncParamOrResultArraysIdentical(func1.params, func2.params) &&
134
+ areFuncParamOrResultArraysIdentical(func1.results, func2.results));
135
+ }
136
+ function areMethodArgsArraysIdentical(args1, args2) {
137
+ if (args1 === undefined && args2 === undefined)
138
+ return true;
139
+ if (args1 === undefined || args2 === undefined)
140
+ return false;
141
+ if (args1.length !== args2.length)
142
+ return false;
143
+ for (let i = 0; i < args1.length; i++) {
144
+ // Compare based on type only, names of args/results don't affect signature identity here.
145
+ if (!areTypeInfosIdentical(args1[i].type, args2[i].type)) {
146
+ return false;
147
+ }
148
+ }
149
+ return true;
150
+ }
151
+ export function areTypeInfosIdentical(type1InfoOrName, type2InfoOrName) {
152
+ const t1Norm = normalizeTypeInfo(type1InfoOrName);
153
+ const t2Norm = normalizeTypeInfo(type2InfoOrName);
154
+ if (t1Norm === t2Norm)
155
+ return true; // Object identity
156
+ if (t1Norm.kind !== t2Norm.kind)
157
+ return false;
158
+ // If types have names, the names must match for identity.
159
+ // If one has a name and the other doesn't, they are not identical.
160
+ if (t1Norm.name !== t2Norm.name)
161
+ return false;
162
+ // If both are named and names match, for Basic, Struct, Interface, this is sufficient for identity.
163
+ if (t1Norm.name !== undefined /* && t2Norm.name is also defined and equal */) {
164
+ if (t1Norm.kind === TypeKind.Basic ||
165
+ t1Norm.kind === TypeKind.Struct ||
166
+ t1Norm.kind === TypeKind.Interface) {
167
+ return true;
168
+ }
169
+ }
170
+ // For other types (Pointer, Slice, etc.), or if both are anonymous (name is undefined),
171
+ // structural comparison is needed.
172
+ switch (t1Norm.kind) {
173
+ case TypeKind.Basic:
174
+ // Names matched if they were defined, or both undefined (which means true by t1Norm.name !== t2Norm.name being false)
175
+ return true;
176
+ case TypeKind.Pointer:
177
+ return compareOptionalTypeInfo(t1Norm.elemType, t2Norm.elemType);
178
+ case TypeKind.Slice:
179
+ return compareOptionalTypeInfo(t1Norm.elemType, t2Norm.elemType);
180
+ case TypeKind.Array:
181
+ return (t1Norm.length === t2Norm.length &&
182
+ compareOptionalTypeInfo(t1Norm.elemType, t2Norm.elemType));
183
+ case TypeKind.Map:
184
+ return (compareOptionalTypeInfo(t1Norm.keyType, t2Norm.keyType) &&
185
+ compareOptionalTypeInfo(t1Norm.elemType, t2Norm.elemType));
186
+ case TypeKind.Channel:
187
+ return (
188
+ // Ensure direction property exists before comparing, or handle undefined if it can be
189
+ (t1Norm.direction || 'both') ===
190
+ (t2Norm.direction || 'both') &&
191
+ compareOptionalTypeInfo(t1Norm.elemType, t2Norm.elemType));
192
+ case TypeKind.Function:
193
+ return areFuncSignaturesIdentical(t1Norm, t2Norm);
194
+ case TypeKind.Struct:
195
+ case TypeKind.Interface:
196
+ // If we reach here, names were undefined (both anonymous) or names matched but was not Basic/Struct/Interface.
197
+ // For anonymous Struct/Interface, strict identity means full structural comparison.
198
+ // For now, we consider anonymous types not identical unless they are the same object (caught above).
199
+ // If they were named and matched, 'return true' was hit earlier for these kinds.
200
+ return false;
201
+ default:
202
+ return false;
203
+ }
204
+ }
205
+ /**
206
+ * Validates that a map key matches the expected type info.
207
+ *
208
+ * @param key The key to validate
209
+ * @param keyTypeInfo The normalized type info for the key
210
+ * @returns True if the key matches the type info, false otherwise
211
+ */
212
+ function validateMapKey(key, keyTypeInfo) {
213
+ if (keyTypeInfo.kind === TypeKind.Basic) {
214
+ // For string keys
215
+ if (keyTypeInfo.name === 'string') {
216
+ return typeof key === 'string';
217
+ }
218
+ else if (keyTypeInfo.name === 'int' ||
219
+ keyTypeInfo.name === 'float64' ||
220
+ keyTypeInfo.name === 'number') {
221
+ if (typeof key === 'string') {
222
+ return /^-?\d+(\.\d+)?$/.test(key);
223
+ }
224
+ else {
225
+ return typeof key === 'number';
226
+ }
227
+ }
228
+ }
229
+ return false;
230
+ }
231
+ /**
232
+ * Checks if a value matches a basic type info.
233
+ *
234
+ * @param value The value to check.
235
+ * @param info The basic type info to match against.
236
+ * @returns True if the value matches the basic type, false otherwise.
237
+ */
238
+ function matchesBasicType(value, info) {
239
+ if (info.name === 'string')
240
+ return typeof value === 'string';
241
+ if (info.name === 'number' || info.name === 'int' || info.name === 'float64')
242
+ return typeof value === 'number';
243
+ if (info.name === 'boolean' || info.name === 'bool')
244
+ return typeof value === 'boolean';
245
+ return false;
246
+ }
247
+ /**
248
+ * Checks if a value matches a struct type info.
249
+ *
250
+ * @param value The value to check.
251
+ * @param info The struct type info to match against.
252
+ * @returns True if the value matches the struct type, false otherwise.
253
+ */
254
+ function matchesStructType(value, info) {
255
+ if (!isStructTypeInfo(info))
256
+ return false;
257
+ // For structs, use instanceof with the constructor
258
+ if (info.ctor && value instanceof info.ctor) {
259
+ return true;
260
+ }
261
+ // Check if the value has all methods defined in the struct's TypeInfo
262
+ // This is a structural check, not a signature check here.
263
+ // Signature checks are more relevant for interface satisfaction.
264
+ if (info.methods && typeof value === 'object' && value !== null) {
265
+ const allMethodsExist = info.methods.every((methodSig) => typeof value[methodSig.name] === 'function');
266
+ if (!allMethodsExist) {
267
+ return false;
268
+ }
269
+ // Further signature checking could be added here if needed for struct-to-struct assignability
270
+ }
271
+ if (typeof value === 'object' && value !== null && info.fields) {
272
+ const fieldNames = Object.keys(info.fields || {});
273
+ const valueFields = Object.keys(value);
274
+ const fieldsExist = fieldNames.every((field) => field in value);
275
+ const sameFieldCount = valueFields.length === fieldNames.length;
276
+ const allFieldsInStruct = valueFields.every((field) => fieldNames.includes(field));
277
+ if (fieldsExist && sameFieldCount && allFieldsInStruct) {
278
+ return Object.entries(info.fields).every(([fieldName, fieldType]) => {
279
+ return matchesType(value[fieldName], normalizeTypeInfo(fieldType));
280
+ });
281
+ }
282
+ return false;
283
+ }
284
+ return false;
285
+ }
286
+ /**
287
+ * Checks if a value matches an interface type info by verifying it implements
288
+ * all required methods with compatible signatures.
289
+ *
290
+ * @param value The value to check.
291
+ * @param info The interface type info to match against.
292
+ * @returns True if the value matches the interface type, false otherwise.
293
+ */
294
+ function matchesInterfaceType(value, info) {
295
+ // Check basic conditions first
296
+ if (!isInterfaceTypeInfo(info) ||
297
+ typeof value !== 'object' ||
298
+ value === null) {
299
+ return false;
300
+ }
301
+ // For interfaces, check if the value has all the required methods with compatible signatures
302
+ return info.methods.every((requiredMethodSig) => {
303
+ const actualMethod = value[requiredMethodSig.name];
304
+ // Method must exist and be a function
305
+ if (typeof actualMethod !== 'function') {
306
+ return false;
307
+ }
308
+ // Check parameter count (basic arity check)
309
+ // Note: This is a simplified check as JavaScript functions can have optional/rest parameters
310
+ const declaredParamCount = actualMethod.length;
311
+ const requiredParamCount = requiredMethodSig.args.length;
312
+ // Strict arity checking can be problematic in JS, so we'll be lenient
313
+ // A method with fewer params than required is definitely incompatible
314
+ if (declaredParamCount < requiredParamCount) {
315
+ return false;
316
+ }
317
+ // Check return types if we can determine them
318
+ // This is challenging in JavaScript without runtime type information
319
+ // If the value has a __goTypeName property, it might be a registered type
320
+ // with more type information available
321
+ if (value.__goTypeName) {
322
+ const valueTypeInfo = typeRegistry.get(value.__goTypeName);
323
+ if (valueTypeInfo && isStructTypeInfo(valueTypeInfo)) {
324
+ // Find the matching method in the value's type info
325
+ const valueMethodSig = valueTypeInfo.methods.find((m) => m.name === requiredMethodSig.name);
326
+ if (valueMethodSig) {
327
+ // Compare return types
328
+ if (valueMethodSig.returns.length !== requiredMethodSig.returns.length) {
329
+ return false;
330
+ }
331
+ // Compare each return type for compatibility
332
+ for (let i = 0; i < requiredMethodSig.returns.length; i++) {
333
+ const requiredReturnType = normalizeTypeInfo(requiredMethodSig.returns[i].type);
334
+ const valueReturnType = normalizeTypeInfo(valueMethodSig.returns[i].type);
335
+ // For interface return types, we need to check if the value's return type
336
+ // implements the required interface
337
+ if (isInterfaceTypeInfo(requiredReturnType)) {
338
+ // This would be a recursive check, but we'll simplify for now
339
+ // by just checking if the types are the same or if the value type
340
+ // is registered as implementing the interface
341
+ if (requiredReturnType.name !== valueReturnType.name) {
342
+ // Check if valueReturnType implements requiredReturnType
343
+ // This would require additional implementation tracking
344
+ return false;
345
+ }
346
+ }
347
+ // For non-interface types, check direct type compatibility
348
+ else if (requiredReturnType.name !== valueReturnType.name) {
349
+ return false;
350
+ }
351
+ }
352
+ // Similarly, we could check parameter types for compatibility
353
+ // but we'll skip that for brevity
354
+ }
355
+ }
356
+ }
357
+ // If we can't determine detailed type information, we'll accept the method
358
+ // as long as it exists with a compatible arity
359
+ return true;
360
+ });
361
+ }
362
+ /**
363
+ * Checks if a value matches a map type info.
364
+ *
365
+ * @param value The value to check.
366
+ * @param info The map type info to match against.
367
+ * @returns True if the value matches the map type, false otherwise.
368
+ */
369
+ function matchesMapType(value, info) {
370
+ if (typeof value !== 'object' || value === null)
371
+ return false;
372
+ if (!isMapTypeInfo(info))
373
+ return false;
374
+ if (info.keyType || info.elemType) {
375
+ let entries = [];
376
+ if (value instanceof Map) {
377
+ entries = Array.from(value.entries());
378
+ }
379
+ else {
380
+ entries = Object.entries(value);
381
+ }
382
+ if (entries.length === 0)
383
+ return true; // Empty map matches any map type
384
+ const sampleSize = Math.min(5, entries.length);
385
+ for (let i = 0; i < sampleSize; i++) {
386
+ const [k, v] = entries[i];
387
+ if (info.keyType) {
388
+ if (!validateMapKey(k, normalizeTypeInfo(info.keyType))) {
389
+ return false;
390
+ }
391
+ }
392
+ if (info.elemType &&
393
+ !matchesType(v, normalizeTypeInfo(info.elemType))) {
394
+ return false;
395
+ }
396
+ }
397
+ }
398
+ return true;
399
+ }
400
+ /**
401
+ * Checks if a value matches an array or slice type info.
402
+ *
403
+ * @param value The value to check.
404
+ * @param info The array or slice type info to match against.
405
+ * @returns True if the value matches the array or slice type, false otherwise.
406
+ */
407
+ function matchesArrayOrSliceType(value, info) {
408
+ // For slices and arrays, check if the value is an array and sample element types
409
+ if (!Array.isArray(value))
410
+ return false;
411
+ if (!isArrayTypeInfo(info) && !isSliceTypeInfo(info))
412
+ return false;
413
+ if (info.elemType) {
414
+ const arr = value;
415
+ if (arr.length === 0)
416
+ return true; // Empty array matches any array type
417
+ const sampleSize = Math.min(5, arr.length);
418
+ for (let i = 0; i < sampleSize; i++) {
419
+ if (!matchesType(arr[i], normalizeTypeInfo(info.elemType))) {
420
+ return false;
421
+ }
422
+ }
423
+ }
424
+ return true;
425
+ }
426
+ /**
427
+ * Checks if a value matches a pointer type info.
428
+ *
429
+ * @param value The value to check.
430
+ * @param info The pointer type info to match against.
431
+ * @returns True if the value matches the pointer type, false otherwise.
432
+ */
433
+ function matchesPointerType(value, info) {
434
+ // Allow null/undefined values to match pointer types to support nil pointer assertions
435
+ // This enables Go's nil pointer type assertions like `ptr, ok := i.(*SomeType)` to work correctly
436
+ if (value === null || value === undefined) {
437
+ return true;
438
+ }
439
+ // Check if the value is a VarRef (has a 'value' property)
440
+ if (typeof value !== 'object' || !('value' in value)) {
441
+ return false;
442
+ }
443
+ if (!isPointerTypeInfo(info))
444
+ return false;
445
+ if (info.elemType) {
446
+ const elemTypeInfo = normalizeTypeInfo(info.elemType);
447
+ return matchesType(value.value, elemTypeInfo);
448
+ }
449
+ return true;
450
+ }
451
+ /**
452
+ * Checks if a value matches a function type info.
453
+ *
454
+ * @param value The value to check.
455
+ * @param info The function type info to match against.
456
+ * @returns True if the value matches the function type, false otherwise.
457
+ */
458
+ function matchesFunctionType(value, info) {
459
+ // First check if the value is a function
460
+ if (typeof value !== 'function') {
461
+ return false;
462
+ }
463
+ // This is important for named function types
464
+ if (info.name && value.__goTypeName) {
465
+ return info.name === value.__goTypeName;
466
+ }
467
+ return true;
468
+ }
469
+ /**
470
+ * Checks if a value matches a channel type info.
471
+ *
472
+ * @param value The value to check.
473
+ * @param info The channel type info to match against.
474
+ * @returns True if the value matches the channel type, false otherwise.
475
+ */
476
+ function matchesChannelType(value, info) {
477
+ // First check if it's a channel or channel reference
478
+ if (typeof value !== 'object' || value === null) {
479
+ return false;
480
+ }
481
+ // If it's a ChannelRef, get the underlying channel
482
+ let channel = value;
483
+ let valueDirection = 'both';
484
+ if ('channel' in value && 'direction' in value) {
485
+ channel = value.channel;
486
+ valueDirection = value.direction;
487
+ }
488
+ // Check if it has channel methods
489
+ if (!('send' in channel) ||
490
+ !('receive' in channel) ||
491
+ !('close' in channel) ||
492
+ typeof channel.send !== 'function' ||
493
+ typeof channel.receive !== 'function' ||
494
+ typeof channel.close !== 'function') {
495
+ return false;
496
+ }
497
+ if (info.elemType) {
498
+ if (info.elemType === 'string' &&
499
+ 'zeroValue' in channel &&
500
+ channel.zeroValue !== '') {
501
+ return false;
502
+ }
503
+ if (info.elemType === 'number' &&
504
+ 'zeroValue' in channel &&
505
+ typeof channel.zeroValue !== 'number') {
506
+ return false;
507
+ }
508
+ }
509
+ if (info.direction) {
510
+ return valueDirection === info.direction;
511
+ }
512
+ return true;
513
+ }
514
+ /**
515
+ * Checks if a value matches a type info.
516
+ *
517
+ * @param value The value to check.
518
+ * @param info The type info to match against.
519
+ * @returns True if the value matches the type info, false otherwise.
520
+ */
521
+ function matchesType(value, info) {
522
+ if (value === null || value === undefined) {
523
+ return false;
524
+ }
525
+ switch (info.kind) {
526
+ case TypeKind.Basic:
527
+ return matchesBasicType(value, info);
528
+ case TypeKind.Struct:
529
+ return matchesStructType(value, info);
530
+ case TypeKind.Interface:
531
+ return matchesInterfaceType(value, info);
532
+ case TypeKind.Map:
533
+ return matchesMapType(value, info);
534
+ case TypeKind.Slice:
535
+ case TypeKind.Array:
536
+ return matchesArrayOrSliceType(value, info);
537
+ case TypeKind.Pointer:
538
+ return matchesPointerType(value, info);
539
+ case TypeKind.Function:
540
+ return matchesFunctionType(value, info);
541
+ case TypeKind.Channel:
542
+ return matchesChannelType(value, info);
543
+ default:
544
+ console.warn(`Type matching for kind '${info.kind}' not implemented.`);
545
+ return false;
546
+ }
547
+ }
548
+ /**
549
+ * Performs a type assertion on a value against a specified type.
550
+ * Returns an object containing the value (cast to type T) and a boolean indicating success.
551
+ * This is used to implement Go's type assertion with comma-ok idiom: value, ok := x.(Type)
552
+ *
553
+ * @param value The value to check against the type
554
+ * @param typeInfo The type information to check against (can be a string name or TypeInfo object)
555
+ * @returns An object with the asserted value and a boolean indicating if the assertion succeeded
556
+ */
557
+ export function typeAssert(value, typeInfo) {
558
+ const normalizedType = normalizeTypeInfo(typeInfo);
559
+ if (isPointerTypeInfo(normalizedType) && value === null) {
560
+ return { value: null, ok: true };
561
+ }
562
+ if (isStructTypeInfo(normalizedType) &&
563
+ normalizedType.methods &&
564
+ normalizedType.methods.length > 0 &&
565
+ typeof value === 'object' &&
566
+ value !== null) {
567
+ // Check if the value implements all methods of the struct type with compatible signatures.
568
+ // This is more for interface satisfaction by a struct.
569
+ // For struct-to-struct assertion, usually instanceof or field checks are primary.
570
+ const allMethodsMatch = normalizedType.methods.every((requiredMethodSig) => {
571
+ const actualMethod = value[requiredMethodSig.name];
572
+ if (typeof actualMethod !== 'function') {
573
+ return false;
574
+ }
575
+ const valueTypeInfoVal = value.$typeInfo;
576
+ if (valueTypeInfoVal) {
577
+ const normalizedValueType = normalizeTypeInfo(valueTypeInfoVal);
578
+ if (isStructTypeInfo(normalizedValueType) ||
579
+ isInterfaceTypeInfo(normalizedValueType)) {
580
+ const actualValueMethodSig = normalizedValueType.methods.find((m) => m.name === requiredMethodSig.name);
581
+ if (actualValueMethodSig) {
582
+ // Perform full signature comparison using MethodSignatures
583
+ const paramsMatch = areMethodArgsArraysIdentical(requiredMethodSig.args, actualValueMethodSig.args);
584
+ const resultsMatch = areMethodArgsArraysIdentical(requiredMethodSig.returns, actualValueMethodSig.returns);
585
+ return paramsMatch && resultsMatch;
586
+ }
587
+ else {
588
+ // Value has TypeInfo listing methods, but this specific method isn't listed.
589
+ // This implies a mismatch for strict signature check based on TypeInfo.
590
+ return false;
591
+ }
592
+ }
593
+ }
594
+ // If the function exists and there's no type info, assume it matches.
595
+ // TODO check this
596
+ return true;
597
+ });
598
+ if (allMethodsMatch) {
599
+ return { value: value, ok: true };
600
+ }
601
+ }
602
+ if (isStructTypeInfo(normalizedType) &&
603
+ normalizedType.fields &&
604
+ typeof value === 'object' &&
605
+ value !== null) {
606
+ const fieldNames = Object.keys(normalizedType.fields);
607
+ const valueFields = Object.keys(value);
608
+ // For struct type assertions, we need exact field matching
609
+ const structFieldsMatch = fieldNames.length === valueFields.length &&
610
+ fieldNames.every((field) => field in value) &&
611
+ valueFields.every((field) => fieldNames.includes(field));
612
+ if (structFieldsMatch) {
613
+ const typesMatch = Object.entries(normalizedType.fields).every(([fieldName, fieldType]) => {
614
+ return matchesType(value[fieldName], normalizeTypeInfo(fieldType));
615
+ });
616
+ return { value: value, ok: typesMatch };
617
+ }
618
+ else {
619
+ return { value: null, ok: false };
620
+ }
621
+ }
622
+ if (isMapTypeInfo(normalizedType) &&
623
+ typeof value === 'object' &&
624
+ value !== null) {
625
+ if (normalizedType.keyType || normalizedType.elemType) {
626
+ let entries = [];
627
+ if (value instanceof Map) {
628
+ entries = Array.from(value.entries());
629
+ }
630
+ else {
631
+ entries = Object.entries(value);
632
+ }
633
+ if (entries.length === 0) {
634
+ return { value: value, ok: true };
635
+ }
636
+ const sampleSize = Math.min(5, entries.length);
637
+ for (let i = 0; i < sampleSize; i++) {
638
+ const [k, v] = entries[i];
639
+ if (normalizedType.keyType) {
640
+ if (!validateMapKey(k, normalizeTypeInfo(normalizedType.keyType))) {
641
+ return { value: null, ok: false };
642
+ }
643
+ }
644
+ if (normalizedType.elemType) {
645
+ const elemTypeInfo = normalizeTypeInfo(normalizedType.elemType);
646
+ if (!matchesType(v, elemTypeInfo)) {
647
+ return { value: null, ok: false };
648
+ }
649
+ }
650
+ }
651
+ // If we get here, the map type assertion passes
652
+ return { value: value, ok: true };
653
+ }
654
+ }
655
+ const matches = matchesType(value, normalizedType);
656
+ if (matches) {
657
+ return { value: value, ok: true };
658
+ }
659
+ // If we get here, the assertion failed
660
+ // For registered types, use the zero value from the registry
661
+ if (typeof typeInfo === 'string') {
662
+ const registeredType = typeRegistry.get(typeInfo);
663
+ if (registeredType && registeredType.zeroValue !== undefined) {
664
+ return { value: registeredType.zeroValue, ok: false };
665
+ }
666
+ }
667
+ else if (normalizedType.zeroValue !== undefined) {
668
+ return { value: normalizedType.zeroValue, ok: false };
669
+ }
670
+ return { value: null, ok: false };
671
+ }
672
+ /**
673
+ * Performs a type assertion on a value against a specified type.
674
+ * Returns the value (cast to type T) if the assertion is successful,
675
+ * otherwise throws a runtime error.
676
+ * This is used to implement Go's single-value type assertion: value := x.(Type)
677
+ *
678
+ * @param value The value to check against the type
679
+ * @param typeInfo The type information to check against (can be a string name or TypeInfo object)
680
+ * @returns The asserted value if the assertion succeeded
681
+ * @throws Error if the type assertion fails
682
+ */
683
+ export function mustTypeAssert(value, typeInfo) {
684
+ const { value: assertedValue, ok } = typeAssert(value, typeInfo);
685
+ if (!ok) {
686
+ const targetTypeName = typeof typeInfo === 'string' ? typeInfo : (typeInfo.name || JSON.stringify(typeInfo));
687
+ let valueTypeName = typeof value;
688
+ if (value && value.constructor && value.constructor.name) {
689
+ valueTypeName = value.constructor.name;
690
+ }
691
+ if (value === null) {
692
+ valueTypeName = 'nil';
693
+ }
694
+ throw new Error(`inline type conversion panic: value is ${valueTypeName}, not ${targetTypeName}`);
695
+ }
696
+ return assertedValue;
697
+ }
698
+ /**
699
+ * Checks if a value is of a specific type.
700
+ * Similar to typeAssert but only returns a boolean without extracting the value.
701
+ *
702
+ * @param value The value to check
703
+ * @param typeInfo The type information to check against
704
+ * @returns True if the value matches the type, false otherwise
705
+ */
706
+ export function is(value, typeInfo) {
707
+ return matchesType(value, normalizeTypeInfo(typeInfo));
708
+ }
709
+ /**
710
+ * Helper for Go's type switch statement.
711
+ * Executes the body of the first case whose type matches the value.
712
+ *
713
+ * @param value The value being switched upon.
714
+ * @param cases An array of TypeSwitchCase objects.
715
+ * @param defaultCase Optional function for the default case.
716
+ */
717
+ export function typeSwitch(value, cases, defaultCase) {
718
+ for (const caseObj of cases) {
719
+ // For cases with multiple types (case T1, T2:), use $.is
720
+ if (caseObj.types.length > 1) {
721
+ const matchesAny = caseObj.types.some((typeInfo) => is(value, typeInfo));
722
+ if (matchesAny) {
723
+ // For multi-type cases, the case variable (if any) gets the original value
724
+ caseObj.body(value);
725
+ return; // Found a match, exit switch
726
+ }
727
+ }
728
+ else if (caseObj.types.length === 1) {
729
+ // For single-type cases (case T:), use $.typeAssert to get the typed value and ok status
730
+ const typeInfo = caseObj.types[0];
731
+ const { value: assertedValue, ok } = typeAssert(value, typeInfo);
732
+ if (ok) {
733
+ // Pass the asserted value to the case body function
734
+ caseObj.body(assertedValue);
735
+ return; // Found a match, exit switch
736
+ }
737
+ }
738
+ // Note: Cases with 0 types are not valid in Go type switches
739
+ }
740
+ // If no case matched and a default case exists, execute it
741
+ if (defaultCase) {
742
+ defaultCase();
743
+ }
744
+ }
745
+ //# sourceMappingURL=type.js.map