goscript 0.0.61 → 0.0.63

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 (92) hide show
  1. package/README.md +62 -46
  2. package/compiler/analysis.go +621 -19
  3. package/compiler/analysis_test.go +3 -3
  4. package/compiler/assignment.go +100 -0
  5. package/compiler/builtin_test.go +1 -1
  6. package/compiler/compiler.go +76 -16
  7. package/compiler/compiler_test.go +9 -9
  8. package/compiler/composite-lit.go +29 -8
  9. package/compiler/decl.go +20 -11
  10. package/compiler/expr-call-async.go +26 -1
  11. package/compiler/expr-call-builtins.go +60 -4
  12. package/compiler/expr-call-type-conversion.go +37 -5
  13. package/compiler/expr-call.go +26 -6
  14. package/compiler/expr-selector.go +35 -2
  15. package/compiler/expr-type.go +12 -2
  16. package/compiler/expr.go +61 -0
  17. package/compiler/index.test.ts +3 -1
  18. package/compiler/lit.go +13 -4
  19. package/compiler/spec-struct.go +30 -8
  20. package/compiler/spec-value.go +2 -2
  21. package/compiler/spec.go +23 -4
  22. package/compiler/stmt-assign.go +124 -0
  23. package/compiler/stmt-range.go +2 -2
  24. package/compiler/stmt.go +160 -14
  25. package/compiler/type-info.go +3 -5
  26. package/compiler/type-utils.go +40 -1
  27. package/compiler/type.go +52 -14
  28. package/dist/gs/builtin/builtin.d.ts +8 -1
  29. package/dist/gs/builtin/builtin.js +26 -1
  30. package/dist/gs/builtin/builtin.js.map +1 -1
  31. package/dist/gs/builtin/errors.d.ts +1 -0
  32. package/dist/gs/builtin/errors.js +8 -0
  33. package/dist/gs/builtin/errors.js.map +1 -1
  34. package/dist/gs/builtin/slice.d.ts +5 -4
  35. package/dist/gs/builtin/slice.js +88 -51
  36. package/dist/gs/builtin/slice.js.map +1 -1
  37. package/dist/gs/builtin/type.d.ts +23 -2
  38. package/dist/gs/builtin/type.js +125 -0
  39. package/dist/gs/builtin/type.js.map +1 -1
  40. package/dist/gs/builtin/varRef.d.ts +3 -0
  41. package/dist/gs/builtin/varRef.js +6 -1
  42. package/dist/gs/builtin/varRef.js.map +1 -1
  43. package/dist/gs/bytes/reader.gs.d.ts +1 -1
  44. package/dist/gs/bytes/reader.gs.js +1 -1
  45. package/dist/gs/bytes/reader.gs.js.map +1 -1
  46. package/dist/gs/reflect/index.d.ts +2 -2
  47. package/dist/gs/reflect/index.js +1 -1
  48. package/dist/gs/reflect/index.js.map +1 -1
  49. package/dist/gs/reflect/map.d.ts +3 -2
  50. package/dist/gs/reflect/map.js +37 -3
  51. package/dist/gs/reflect/map.js.map +1 -1
  52. package/dist/gs/reflect/type.d.ts +53 -12
  53. package/dist/gs/reflect/type.js +906 -31
  54. package/dist/gs/reflect/type.js.map +1 -1
  55. package/dist/gs/reflect/types.d.ts +11 -12
  56. package/dist/gs/reflect/types.js +26 -15
  57. package/dist/gs/reflect/types.js.map +1 -1
  58. package/dist/gs/reflect/value.d.ts +4 -4
  59. package/dist/gs/reflect/value.js +8 -2
  60. package/dist/gs/reflect/value.js.map +1 -1
  61. package/dist/gs/slices/slices.d.ts +21 -0
  62. package/dist/gs/slices/slices.js +48 -0
  63. package/dist/gs/slices/slices.js.map +1 -1
  64. package/dist/gs/strconv/atoi.gs.js +20 -2
  65. package/dist/gs/strconv/atoi.gs.js.map +1 -1
  66. package/dist/gs/sync/atomic/type.gs.d.ts +3 -3
  67. package/dist/gs/sync/atomic/type.gs.js +13 -7
  68. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  69. package/dist/gs/unicode/utf8/utf8.d.ts +2 -2
  70. package/dist/gs/unicode/utf8/utf8.js +10 -6
  71. package/dist/gs/unicode/utf8/utf8.js.map +1 -1
  72. package/go.mod +6 -6
  73. package/go.sum +12 -8
  74. package/gs/builtin/builtin.ts +27 -2
  75. package/gs/builtin/errors.ts +12 -0
  76. package/gs/builtin/slice.ts +126 -55
  77. package/gs/builtin/type.ts +159 -2
  78. package/gs/builtin/varRef.ts +8 -2
  79. package/gs/bytes/reader.gs.ts +2 -2
  80. package/gs/math/hypot.gs.test.ts +3 -1
  81. package/gs/math/pow10.gs.test.ts +5 -4
  82. package/gs/reflect/index.ts +3 -2
  83. package/gs/reflect/map.test.ts +7 -6
  84. package/gs/reflect/map.ts +49 -7
  85. package/gs/reflect/type.ts +1150 -57
  86. package/gs/reflect/types.ts +34 -21
  87. package/gs/reflect/value.ts +12 -6
  88. package/gs/slices/slices.ts +55 -0
  89. package/gs/strconv/atoi.gs.ts +18 -2
  90. package/gs/sync/atomic/type.gs.ts +15 -10
  91. package/gs/unicode/utf8/utf8.ts +12 -8
  92. package/package.json +23 -14
@@ -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,
@@ -868,6 +891,110 @@ function matchesType(value: any, info: TypeInfo): boolean {
868
891
  }
869
892
  }
870
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
+
871
998
  /**
872
999
  * Performs a type assertion on a value against a specified type.
873
1000
  * Returns an object containing the value (cast to type T) and a boolean indicating success.
@@ -882,6 +1009,21 @@ export function typeAssert<T>(
882
1009
  typeInfo: string | TypeInfo,
883
1010
  ): TypeAssertResult<T> {
884
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
+
885
1027
  if (isPointerTypeInfo(normalizedType) && value === null) {
886
1028
  return { value: null as unknown as T, ok: true }
887
1029
  }
@@ -1061,3 +1203,18 @@ export function typeSwitch(
1061
1203
  defaultCase()
1062
1204
  }
1063
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
+ }
@@ -5,13 +5,19 @@
5
5
  * var myVariable int // variable referenced
6
6
  * myOtherVar := &myVariable
7
7
  */
8
- export type VarRef<T> = { value: T }
8
+ export type VarRef<T> = { value: T; __isVarRef?: true }
9
9
 
10
10
  /** Wrap a non-null T in a variable reference. */
11
11
  export function varRef<T>(v: T): VarRef<T> {
12
12
  // We create a new object wrapper for every varRef call to ensure
13
13
  // distinct pointer identity, crucial for pointer comparisons (p1 == p2).
14
- return { value: v }
14
+ // The __isVarRef marker allows the reflect system to identify this as a pointer type.
15
+ return { value: v, __isVarRef: true }
16
+ }
17
+
18
+ /** Check if a value is a VarRef (pointer) */
19
+ export function isVarRef(v: unknown): v is VarRef<unknown> {
20
+ return v !== null && typeof v === 'object' && (v as any).__isVarRef === true
15
21
  }
16
22
 
17
23
  /** Dereference a variable reference, throws on null → simulates Go panic. */
@@ -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
 
@@ -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
  })
@@ -35,19 +35,20 @@ export { Swapper } from './swapper.js'
35
35
  // Export new types and constants
36
36
  export {
37
37
  StructTag,
38
+ StructTag_Get,
38
39
  StructField,
39
40
  ValueError,
40
- SelectDir,
41
41
  SelectSend,
42
42
  SelectRecv,
43
43
  SelectDefault,
44
+ SelectCase,
44
45
  bitVector,
45
46
  } from './types.js'
46
47
  export type {
47
48
  uintptr,
48
49
  Pointer,
49
50
  Method,
50
- SelectCase,
51
+ SelectDir,
51
52
  SliceHeader,
52
53
  StringHeader,
53
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 {