goscript 0.0.60 → 0.0.62

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 (87) hide show
  1. package/README.md +9 -0
  2. package/compiler/analysis.go +974 -369
  3. package/compiler/assignment.go +72 -0
  4. package/compiler/compiler.go +74 -15
  5. package/compiler/composite-lit.go +29 -8
  6. package/compiler/decl.go +67 -98
  7. package/compiler/expr-call-async.go +26 -1
  8. package/compiler/expr-call-builtins.go +60 -4
  9. package/compiler/expr-call-helpers.go +182 -0
  10. package/compiler/expr-call-type-conversion.go +37 -5
  11. package/compiler/expr-call.go +25 -33
  12. package/compiler/expr-selector.go +71 -1
  13. package/compiler/expr-type.go +49 -3
  14. package/compiler/expr.go +37 -28
  15. package/compiler/index.test.ts +3 -1
  16. package/compiler/lit.go +13 -4
  17. package/compiler/spec-struct.go +42 -9
  18. package/compiler/spec-value.go +2 -2
  19. package/compiler/spec.go +42 -5
  20. package/compiler/stmt-assign.go +71 -0
  21. package/compiler/stmt-range.go +2 -2
  22. package/compiler/stmt.go +130 -10
  23. package/compiler/type-utils.go +40 -16
  24. package/compiler/type.go +50 -12
  25. package/dist/gs/builtin/builtin.d.ts +8 -1
  26. package/dist/gs/builtin/builtin.js +26 -1
  27. package/dist/gs/builtin/builtin.js.map +1 -1
  28. package/dist/gs/builtin/errors.d.ts +1 -0
  29. package/dist/gs/builtin/errors.js +8 -0
  30. package/dist/gs/builtin/errors.js.map +1 -1
  31. package/dist/gs/builtin/slice.d.ts +5 -4
  32. package/dist/gs/builtin/slice.js +51 -21
  33. package/dist/gs/builtin/slice.js.map +1 -1
  34. package/dist/gs/builtin/type.d.ts +28 -2
  35. package/dist/gs/builtin/type.js +132 -0
  36. package/dist/gs/builtin/type.js.map +1 -1
  37. package/dist/gs/bytes/reader.gs.d.ts +1 -1
  38. package/dist/gs/bytes/reader.gs.js +1 -1
  39. package/dist/gs/bytes/reader.gs.js.map +1 -1
  40. package/dist/gs/internal/byteorder/index.d.ts +6 -0
  41. package/dist/gs/internal/byteorder/index.js +34 -0
  42. package/dist/gs/internal/byteorder/index.js.map +1 -1
  43. package/dist/gs/reflect/index.d.ts +3 -3
  44. package/dist/gs/reflect/index.js +2 -2
  45. package/dist/gs/reflect/index.js.map +1 -1
  46. package/dist/gs/reflect/map.d.ts +3 -2
  47. package/dist/gs/reflect/map.js +37 -3
  48. package/dist/gs/reflect/map.js.map +1 -1
  49. package/dist/gs/reflect/type.d.ts +55 -8
  50. package/dist/gs/reflect/type.js +889 -23
  51. package/dist/gs/reflect/type.js.map +1 -1
  52. package/dist/gs/reflect/types.d.ts +11 -12
  53. package/dist/gs/reflect/types.js +26 -15
  54. package/dist/gs/reflect/types.js.map +1 -1
  55. package/dist/gs/reflect/value.d.ts +4 -4
  56. package/dist/gs/reflect/value.js +8 -2
  57. package/dist/gs/reflect/value.js.map +1 -1
  58. package/dist/gs/slices/slices.d.ts +32 -0
  59. package/dist/gs/slices/slices.js +81 -0
  60. package/dist/gs/slices/slices.js.map +1 -1
  61. package/dist/gs/sync/atomic/type.gs.d.ts +2 -2
  62. package/dist/gs/sync/atomic/type.gs.js +12 -2
  63. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  64. package/dist/gs/unicode/utf8/utf8.d.ts +2 -2
  65. package/dist/gs/unicode/utf8/utf8.js +10 -6
  66. package/dist/gs/unicode/utf8/utf8.js.map +1 -1
  67. package/go.mod +4 -4
  68. package/go.sum +8 -16
  69. package/gs/builtin/builtin.ts +27 -2
  70. package/gs/builtin/errors.ts +12 -0
  71. package/gs/builtin/slice.ts +77 -14
  72. package/gs/builtin/type.ts +167 -2
  73. package/gs/bytes/reader.gs.ts +2 -2
  74. package/gs/internal/byteorder/index.ts +40 -0
  75. package/gs/math/hypot.gs.test.ts +3 -1
  76. package/gs/math/pow10.gs.test.ts +5 -4
  77. package/gs/reflect/index.ts +6 -3
  78. package/gs/reflect/map.test.ts +7 -6
  79. package/gs/reflect/map.ts +49 -7
  80. package/gs/reflect/type.ts +1139 -43
  81. package/gs/reflect/types.ts +34 -21
  82. package/gs/reflect/value.ts +12 -6
  83. package/gs/slices/slices.ts +92 -0
  84. package/gs/sync/atomic/type.gs.ts +14 -5
  85. package/gs/sync/meta.json +1 -1
  86. package/gs/unicode/utf8/utf8.ts +12 -8
  87. package/package.json +13 -13
@@ -39,6 +39,14 @@ export interface MethodSignature {
39
39
  returns: MethodArg[]
40
40
  }
41
41
 
42
+ /**
43
+ * Information about a struct field including type and optional tag
44
+ */
45
+ export interface StructFieldInfo {
46
+ type: TypeInfo | string // The field's type
47
+ tag?: string // The struct field tag (e.g., `json:"name,omitempty"`)
48
+ }
49
+
42
50
  /**
43
51
  * Type information for struct types
44
52
  */
@@ -46,7 +54,7 @@ export interface StructTypeInfo extends BaseTypeInfo {
46
54
  kind: TypeKind.Struct
47
55
  methods: MethodSignature[] // Array of method signatures
48
56
  ctor?: new (...args: any[]) => any
49
- fields: Record<string, TypeInfo | string> // Field names and types for struct fields
57
+ fields: Record<string, TypeInfo | string | StructFieldInfo> // Field names and types for struct fields
50
58
  }
51
59
 
52
60
  /**
@@ -170,6 +178,21 @@ export function isChannelTypeInfo(info: TypeInfo): info is ChannelTypeInfo {
170
178
  return info.kind === TypeKind.Channel
171
179
  }
172
180
 
181
+ /**
182
+ * Type guard to check if a field value is a StructFieldInfo (has 'type' property)
183
+ * vs a direct TypeInfo or string
184
+ */
185
+ export function isStructFieldInfo(
186
+ fieldValue: TypeInfo | string | StructFieldInfo,
187
+ ): fieldValue is StructFieldInfo {
188
+ return (
189
+ typeof fieldValue === 'object' &&
190
+ fieldValue !== null &&
191
+ 'type' in fieldValue &&
192
+ !('kind' in fieldValue)
193
+ )
194
+ }
195
+
173
196
  /**
174
197
  * Comparable interface for Go's comparable constraint.
175
198
  * Types that implement this can be compared with == and !=.
@@ -196,7 +219,7 @@ export const registerStructType = (
196
219
  zeroValue: any,
197
220
  methods: MethodSignature[],
198
221
  ctor: new (...args: any[]) => any,
199
- fields: Record<string, TypeInfo | string> = {},
222
+ fields: Record<string, TypeInfo | string | StructFieldInfo> = {},
200
223
  ): StructTypeInfo => {
201
224
  const typeInfo: StructTypeInfo = {
202
225
  name,
@@ -233,6 +256,14 @@ export const registerInterfaceType = (
233
256
  return typeInfo
234
257
  }
235
258
 
259
+ /**
260
+ * Gets a registered type by name from the type registry.
261
+ * Returns undefined if the type is not registered.
262
+ */
263
+ export const getTypeByName = (name: string): TypeInfo | undefined => {
264
+ return typeRegistry.get(name)
265
+ }
266
+
236
267
  /**
237
268
  * Represents the result of a type assertion.
238
269
  */
@@ -860,6 +891,110 @@ function matchesType(value: any, info: TypeInfo): boolean {
860
891
  }
861
892
  }
862
893
 
894
+ /**
895
+ * Compares a Go type string (from typedNil) with a TypeInfo object.
896
+ * Used to check if a typed nil pointer matches a type assertion target.
897
+ *
898
+ * @param typeStr The Go type string (e.g., "*struct{Name string}")
899
+ * @param typeInfo The normalized TypeInfo to compare against
900
+ * @returns True if the types match, false otherwise
901
+ */
902
+ function compareTypeStringWithTypeInfo(
903
+ typeStr: string,
904
+ typeInfo: TypeInfo,
905
+ ): boolean {
906
+ // For pointer types, strip the leading * and compare element types
907
+ if (isPointerTypeInfo(typeInfo)) {
908
+ if (!typeStr.startsWith('*')) {
909
+ return false
910
+ }
911
+ const elemStr = typeStr.slice(1)
912
+ const elemType = typeInfo.elemType
913
+ if (!elemType) {
914
+ return false
915
+ }
916
+
917
+ // Handle struct types
918
+ if (elemStr.startsWith('struct{')) {
919
+ const elemTypeInfo = normalizeTypeInfo(elemType)
920
+ if (!isStructTypeInfo(elemTypeInfo)) {
921
+ return false
922
+ }
923
+
924
+ // For anonymous structs, compare the type string representation
925
+ // Extract field definitions from the string
926
+ const fieldsMatch = elemStr.match(/^struct{(.+)}$/)
927
+ if (!fieldsMatch) {
928
+ return false
929
+ }
930
+
931
+ const fieldStr = fieldsMatch[1]
932
+ // Parse fields like "Name string" or "X int; Y string"
933
+ const fieldParts = fieldStr.split(';').map(s => s.trim())
934
+ const parsedFields: Record<string, string> = {}
935
+
936
+ for (const part of fieldParts) {
937
+ // Handle "Name string" format
938
+ const match = part.match(/^(\w+)\s+(.+)$/)
939
+ if (match) {
940
+ const [, fieldName, fieldType] = match
941
+ parsedFields[fieldName] = fieldType.trim()
942
+ }
943
+ }
944
+
945
+ // Compare fields
946
+ const typeInfoFields = elemTypeInfo.fields || {}
947
+ const typeInfoFieldNames = Object.keys(typeInfoFields)
948
+ const parsedFieldNames = Object.keys(parsedFields)
949
+
950
+ if (typeInfoFieldNames.length !== parsedFieldNames.length) {
951
+ return false
952
+ }
953
+
954
+ // Check if all field names match and types are compatible
955
+ for (const fieldName of typeInfoFieldNames) {
956
+ if (!(fieldName in parsedFields)) {
957
+ return false
958
+ }
959
+
960
+ const fieldValue = typeInfoFields[fieldName]
961
+ // Handle StructFieldInfo (which has 'type' and optional 'tag' properties)
962
+ const fieldTypeInfo: TypeInfo | string = isStructFieldInfo(fieldValue)
963
+ ? fieldValue.type
964
+ : fieldValue
965
+ const typeInfoFieldType = normalizeTypeInfo(fieldTypeInfo)
966
+ const parsedFieldType = parsedFields[fieldName]
967
+
968
+ // Compare basic types
969
+ if (isBasicTypeInfo(typeInfoFieldType)) {
970
+ const expectedTypeName = typeInfoFieldType.name || ''
971
+ // Map Go types to TypeScript/runtime types
972
+ if (expectedTypeName === 'string' && parsedFieldType === 'string') {
973
+ continue
974
+ }
975
+ if ((expectedTypeName === 'int' || expectedTypeName === 'number') &&
976
+ (parsedFieldType === 'int' || parsedFieldType === 'number')) {
977
+ continue
978
+ }
979
+ return false
980
+ }
981
+ }
982
+
983
+ return true
984
+ }
985
+
986
+ // Handle named types
987
+ if (typeof elemType === 'string') {
988
+ return elemStr === elemType
989
+ }
990
+ if (elemType.name) {
991
+ return elemStr === elemType.name
992
+ }
993
+ }
994
+
995
+ return false
996
+ }
997
+
863
998
  /**
864
999
  * Performs a type assertion on a value against a specified type.
865
1000
  * Returns an object containing the value (cast to type T) and a boolean indicating success.
@@ -874,6 +1009,21 @@ export function typeAssert<T>(
874
1009
  typeInfo: string | TypeInfo,
875
1010
  ): TypeAssertResult<T> {
876
1011
  const normalizedType = normalizeTypeInfo(typeInfo)
1012
+
1013
+ // Handle typed nil pointers (created by typedNil() for conversions like (*T)(nil))
1014
+ if (typeof value === 'object' && value !== null && value.__isTypedNil) {
1015
+ // For typed nils, we need to compare the stored type with the expected type
1016
+ if (isPointerTypeInfo(normalizedType)) {
1017
+ // Parse the stored type string and compare with expected type
1018
+ const storedTypeStr = value.__goType as string
1019
+ if (compareTypeStringWithTypeInfo(storedTypeStr, normalizedType)) {
1020
+ return { value: null as unknown as T, ok: true }
1021
+ }
1022
+ return { value: null as unknown as T, ok: false }
1023
+ }
1024
+ return { value: null as unknown as T, ok: false }
1025
+ }
1026
+
877
1027
  if (isPointerTypeInfo(normalizedType) && value === null) {
878
1028
  return { value: null as unknown as T, ok: true }
879
1029
  }
@@ -1053,3 +1203,18 @@ export function typeSwitch(
1053
1203
  defaultCase()
1054
1204
  }
1055
1205
  }
1206
+
1207
+ /**
1208
+ * Creates a typed nil pointer with type metadata for reflection.
1209
+ * This is used for type conversions like (*Interface)(nil) where we need
1210
+ * to preserve the pointer type information even though the value is null.
1211
+ *
1212
+ * @param typeName The full Go type name (e.g., "*main.Stringer")
1213
+ * @returns An object that represents a typed nil with reflection metadata
1214
+ */
1215
+ export function typedNil(typeName: string): any {
1216
+ return Object.assign(Object.create(null), {
1217
+ __goType: typeName,
1218
+ __isTypedNil: true,
1219
+ })
1220
+ }
@@ -224,7 +224,7 @@ export class Reader {
224
224
  }
225
225
 
226
226
  // NewReader returns a new [Reader] reading from b.
227
- export function NewReader(b: $.Bytes): Reader | null {
228
- return new Reader({})
227
+ export function NewReader(b: $.Bytes): Reader {
228
+ return new Reader({s: $.normalizeBytes(b), i: 0, prevRune: -1})
229
229
  }
230
230
 
@@ -38,3 +38,43 @@ export function LEUint64(b: $.Bytes): number {
38
38
  let high = LEUint32($.goSlice(b, 4, undefined))
39
39
  return low + high * 0x100000000
40
40
  }
41
+
42
+ // Big Endian Put functions
43
+ export function BEPutUint16(b: $.Bytes, v: number): void {
44
+ b![0] = (v >> 8) & 0xff
45
+ b![1] = v & 0xff
46
+ }
47
+
48
+ export function BEPutUint32(b: $.Bytes, v: number): void {
49
+ b![0] = (v >> 24) & 0xff
50
+ b![1] = (v >> 16) & 0xff
51
+ b![2] = (v >> 8) & 0xff
52
+ b![3] = v & 0xff
53
+ }
54
+
55
+ export function BEPutUint64(b: $.Bytes, v: number): void {
56
+ const high = Math.floor(v / 0x100000000)
57
+ const low = v >>> 0
58
+ BEPutUint32(b, high)
59
+ BEPutUint32($.goSlice(b, 4, undefined), low)
60
+ }
61
+
62
+ // Little Endian Put functions
63
+ export function LEPutUint16(b: $.Bytes, v: number): void {
64
+ b![0] = v & 0xff
65
+ b![1] = (v >> 8) & 0xff
66
+ }
67
+
68
+ export function LEPutUint32(b: $.Bytes, v: number): void {
69
+ b![0] = v & 0xff
70
+ b![1] = (v >> 8) & 0xff
71
+ b![2] = (v >> 16) & 0xff
72
+ b![3] = (v >> 24) & 0xff
73
+ }
74
+
75
+ export function LEPutUint64(b: $.Bytes, v: number): void {
76
+ const low = v >>> 0
77
+ const high = Math.floor(v / 0x100000000)
78
+ LEPutUint32(b, low)
79
+ LEPutUint32($.goSlice(b, 4, undefined), high)
80
+ }
@@ -43,7 +43,9 @@ describe('Hypot', () => {
43
43
  it('should handle very large values without overflow', () => {
44
44
  const large = 1e150
45
45
  const result = Hypot(large, large)
46
- expect(result).toBeCloseTo(large * Math.sqrt(2), 5)
46
+ const expected = large * Math.sqrt(2)
47
+ // Use relative tolerance for very large numbers
48
+ expect(Math.abs(result - expected) / expected).toBeLessThan(1e-10)
47
49
  expect(IsInf(result, 0)).toBe(false)
48
50
  })
49
51
 
@@ -26,10 +26,11 @@ describe('Pow10', () => {
26
26
  })
27
27
 
28
28
  it('should handle large positive exponents', () => {
29
- expect(Pow10(100)).toBe(1e100)
30
- expect(Pow10(200)).toBe(1e200)
31
- expect(Pow10(300)).toBe(1e300)
32
- expect(Pow10(308)).toBe(1e308)
29
+ // Use relative tolerance for very large numbers
30
+ expect(Math.abs(Pow10(100) - 1e100) / 1e100).toBeLessThan(1e-10)
31
+ expect(Math.abs(Pow10(200) - 1e200) / 1e200).toBeLessThan(1e-10)
32
+ expect(Math.abs(Pow10(300) - 1e300) / 1e300).toBeLessThan(1e-10)
33
+ expect(Math.abs(Pow10(308) - 1e308) / 1e308).toBeLessThan(1e-10)
33
34
  expect(Pow10(309)).toBe(Number.POSITIVE_INFINITY)
34
35
  expect(Pow10(400)).toBe(Number.POSITIVE_INFINITY)
35
36
  })
@@ -1,6 +1,7 @@
1
1
  // Export the main reflect functions organized like Go stdlib
2
2
  export {
3
3
  TypeOf,
4
+ TypeFor,
4
5
  ValueOf,
5
6
  Value,
6
7
  Kind_String,
@@ -14,6 +15,7 @@ export {
14
15
  RecvDir,
15
16
  SendDir,
16
17
  BothDir,
18
+ getInterfaceTypeByName,
17
19
  } from './type.js'
18
20
  export type { Type, ChanDir, Kind } from './type.js'
19
21
  export { DeepEqual } from './deepequal.js'
@@ -33,19 +35,20 @@ export { Swapper } from './swapper.js'
33
35
  // Export new types and constants
34
36
  export {
35
37
  StructTag,
38
+ StructTag_Get,
39
+ StructField,
36
40
  ValueError,
37
- SelectDir,
38
41
  SelectSend,
39
42
  SelectRecv,
40
43
  SelectDefault,
44
+ SelectCase,
41
45
  bitVector,
42
46
  } from './types.js'
43
47
  export type {
44
48
  uintptr,
45
49
  Pointer,
46
- StructField,
47
50
  Method,
48
- SelectCase,
51
+ SelectDir,
49
52
  SliceHeader,
50
53
  StringHeader,
51
54
  MapIter,
@@ -11,20 +11,21 @@ describe('MapIter', () => {
11
11
  const iter = new MapIter<string, number>(map)
12
12
 
13
13
  expect(iter.current?.done === false).toBe(true)
14
- expect(iter.Key()).toBe('one')
15
- expect(iter.Value()).toBe(1)
14
+ // Key() and Value() return reflect.Value objects now
15
+ expect(iter.Key().Interface()).toBe('one')
16
+ expect(iter.Value().Interface()).toBe(1)
16
17
 
17
18
  expect(iter.Next()).toBe(true)
18
19
  expect(iter.current?.done === false).toBe(true)
19
- expect(typeof iter.Key()).toBe('string')
20
- expect(typeof iter.Value()).toBe('number')
20
+ expect(iter.Key().Kind()).toBe(24) // String kind
21
+ expect(iter.Value().Kind()).toBe(2) // Int kind
21
22
 
22
23
  const newMap = new Map<string, number>()
23
24
  newMap.set('reset', 100)
24
25
  iter.Reset(newMap)
25
26
 
26
27
  expect(iter.current?.done === false).toBe(true)
27
- expect(iter.Key()).toBe('reset')
28
- expect(iter.Value()).toBe(100)
28
+ expect(iter.Key().Interface()).toBe('reset')
29
+ expect(iter.Value().Interface()).toBe(100)
29
30
  })
30
31
  })
package/gs/reflect/map.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Type, Kind, Value, Map as MapKind } from './type.js'
1
+ import { Type, Kind, Value, Map as MapKind, StructField, TypeOf } from './type.js'
2
2
 
3
3
  // Simple MapOf implementation using JavaScript Map
4
4
  export function MapOf(key: Type, elem: Type): Type {
@@ -20,26 +20,66 @@ class MapType implements Type {
20
20
  return MapKind // Map kind
21
21
  }
22
22
 
23
+ public Name(): string {
24
+ return '' // Map types are unnamed composite types
25
+ }
26
+
23
27
  public Size(): number {
24
28
  return 8 // pointer size
25
29
  }
26
30
 
27
- public Elem(): Type | null {
31
+ public Elem(): Type {
28
32
  return this._elemType
29
33
  }
30
34
 
31
- public Key(): Type | null {
35
+ public Key(): Type {
32
36
  return this._keyType
33
37
  }
34
38
 
35
39
  public NumField(): number {
36
40
  return 0
37
41
  }
42
+
43
+ public Field(_i: number): StructField {
44
+ throw new Error('reflect: Field of non-struct type map')
45
+ }
46
+
47
+ public Implements(u: Type | null): boolean {
48
+ if (!u) {
49
+ return false
50
+ }
51
+ if (u.Kind() !== 20) {
52
+ // Interface kind
53
+ throw new Error('reflect: non-interface type passed to Type.Implements')
54
+ }
55
+ return false
56
+ }
57
+
58
+ public OverflowInt(_x: number): boolean {
59
+ throw new Error('reflect: OverflowInt of non-integer type map')
60
+ }
61
+
62
+ public OverflowUint(_x: number): boolean {
63
+ throw new Error('reflect: OverflowUint of non-integer type map')
64
+ }
65
+
66
+ public OverflowFloat(_x: number): boolean {
67
+ throw new Error('reflect: OverflowFloat of non-float type map')
68
+ }
69
+
70
+ public NumMethod(): number {
71
+ return 0
72
+ }
73
+
74
+ public Bits(): number {
75
+ throw new Error('reflect: Bits of non-sized type map')
76
+ }
38
77
  }
39
78
 
40
79
  /**
41
80
  * MapIter provides an iterator interface for Go maps.
42
81
  * It wraps a JavaScript Map iterator and provides methods to iterate over key-value pairs.
82
+ * Returns reflect.Value for Key() and Value() to match Go's reflect.MapIter.
43
83
  * @template K - The type of keys in the map
44
84
  * @template V - The type of values in the map
45
85
  */
@@ -57,12 +97,14 @@ export class MapIter<K = unknown, V = unknown> {
57
97
  return !this.current.done
58
98
  }
59
99
 
60
- public Key(): K | null {
61
- return this.current?.value?.[0] ?? null
100
+ public Key(): Value {
101
+ const rawKey = this.current?.value?.[0] ?? null
102
+ return new Value(rawKey, TypeOf(rawKey))
62
103
  }
63
104
 
64
- public Value(): V | null {
65
- return this.current?.value?.[1] ?? null
105
+ public Value(): Value {
106
+ const rawVal = this.current?.value?.[1] ?? null
107
+ return new Value(rawVal, TypeOf(rawVal))
66
108
  }
67
109
 
68
110
  public Reset(m: Map<K, V>): void {