@chenglou/freerange 0.0.1 → 0.0.3

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.
@@ -1,15 +1,22 @@
1
1
  import type {SiteID, ValueID} from '../ir/ids.ts'
2
2
  import type {InstructionIR} from '../ir/instructions.ts'
3
3
  import type {FunctionIR} from '../ir/program.ts'
4
+ import {
5
+ createValueIdentityOwner,
6
+ sameValueIdentity,
7
+ type ValueIdentity,
8
+ type ValueIdentityOwner,
9
+ } from '../domain/value-identity.ts'
4
10
  import type {InferredPrecondition, NumericExpression} from './model.ts'
5
11
 
6
12
  export type ExpressionContext = {
7
13
  parameterExpressions: Array<NumericExpression | null>
8
- // Calls pass the caller's value keys directly, so duplicate arguments and facts created
9
- // in a callee refer to the same identity as the caller. Local keys use a nested namespace
10
- // to avoid colliding with the caller's ValueIDs.
11
- parameterIdentityKeys: string[]
12
- identityNamespace: string
14
+ // Calls pass the caller's identities directly, so duplicate arguments and facts created
15
+ // in a callee refer to the same stored value. Local identities use the evaluation's
16
+ // owner token, which distinguishes separate calls without encoding the call path.
17
+ parameterIdentities: ValueIdentity[]
18
+ identityOwner: ValueIdentityOwner
19
+ identityByValue: Array<ValueIdentity | undefined>
13
20
  parameterIndexByValue: Array<number | undefined>
14
21
  instructionByValue: Array<InstructionIR | undefined>
15
22
  instructionCount: number
@@ -18,21 +25,27 @@ export type ExpressionContext = {
18
25
  export function createExpressionContext(
19
26
  fn: FunctionIR,
20
27
  parameterExpressions: Array<NumericExpression | null>,
21
- parameterIdentityKeys?: string[],
22
- identityNamespace = `${fn.name}/`,
28
+ parameterIdentities?: ValueIdentity[],
29
+ identityOwner = createValueIdentityOwner(),
23
30
  ): ExpressionContext {
24
- const identityKeys = parameterIdentityKeys ?? fn.parameters.map((_, index) => `p${index}`)
25
- if (identityKeys.length !== fn.parameters.length) {
26
- throw new Error(`Expected ${fn.parameters.length} parameter identity keys for ${fn.name}`)
27
- }
28
31
  const context: ExpressionContext = {
29
32
  parameterExpressions,
30
- parameterIdentityKeys: identityKeys,
31
- identityNamespace,
33
+ parameterIdentities: [],
34
+ identityOwner,
35
+ identityByValue: [],
32
36
  parameterIndexByValue: [],
33
37
  instructionByValue: [],
34
38
  instructionCount: 0,
35
39
  }
40
+ context.parameterIdentities = parameterIdentities
41
+ ?? fn.parameters.map(parameter => ({
42
+ kind: 'local',
43
+ owner: identityOwner,
44
+ value: parameter.value,
45
+ }))
46
+ if (context.parameterIdentities.length !== fn.parameters.length) {
47
+ throw new Error(`Expected ${fn.parameters.length} parameter identities for ${fn.name}`)
48
+ }
36
49
  for (let index = 0; index < fn.parameters.length; index++) {
37
50
  context.parameterIndexByValue[fn.parameters[index]!.value] = index
38
51
  }
@@ -159,29 +172,67 @@ export function staticRequirement(
159
172
  return null
160
173
  }
161
174
 
162
- // A stable name for the runtime value an IR value holds. Forward value facts and exact
175
+ // A stable identity for the runtime value an IR value holds. Forward value facts and exact
163
176
  // same-value operations share this rule instead of maintaining separate notions of
164
177
  // identity. Property and array reads are stable under the accepted subset's immutability
165
178
  // rules; module and platform reads stay value-keyed because they may change between reads.
166
- export function canonicalValueKey(value: ValueID, context: ExpressionContext): string {
179
+ export function canonicalValueIdentity(
180
+ value: ValueID,
181
+ context: ExpressionContext,
182
+ ): ValueIdentity {
183
+ const cached = context.identityByValue[value]
184
+ if (cached != null) return cached
167
185
  const stored = resolveStoredValue(value, context)
168
- if (stored !== value) return canonicalValueKey(stored, context)
186
+ if (stored !== value) {
187
+ const identity = canonicalValueIdentity(stored, context)
188
+ context.identityByValue[value] = identity
189
+ return identity
190
+ }
169
191
  const parameterIndex = context.parameterIndexByValue[value]
170
- if (parameterIndex != null) return context.parameterIdentityKeys[parameterIndex] ?? `p${parameterIndex}`
192
+ if (parameterIndex != null) {
193
+ const identity = context.parameterIdentities[parameterIndex]
194
+ if (identity == null) throw new Error(`Missing identity for parameter ${parameterIndex}`)
195
+ context.identityByValue[value] = identity
196
+ return identity
197
+ }
171
198
  const producer = context.instructionByValue[value]
199
+ let identity: ValueIdentity
172
200
  if (producer?.kind === 'property') {
173
- return `${canonicalValueKey(producer.object, context)}.${JSON.stringify(producer.property)}`
174
- }
175
- if (producer?.kind === 'arrayLength') return `${canonicalValueKey(producer.array, context)}.length`
176
- if (producer?.kind === 'stringLength') return `${canonicalValueKey(producer.value, context)}.length`
177
- if (producer?.kind === 'arrayIndex') {
178
- return `${canonicalValueKey(producer.array, context)}[${canonicalValueKey(producer.index, context)}]`
201
+ identity = {
202
+ kind: 'property',
203
+ object: canonicalValueIdentity(producer.object, context),
204
+ property: producer.property,
205
+ }
206
+ } else if (producer?.kind === 'arrayLength') {
207
+ identity = {
208
+ kind: 'property',
209
+ object: canonicalValueIdentity(producer.array, context),
210
+ property: 'length',
211
+ }
212
+ } else if (producer?.kind === 'stringLength') {
213
+ identity = {
214
+ kind: 'property',
215
+ object: canonicalValueIdentity(producer.value, context),
216
+ property: 'length',
217
+ }
218
+ } else if (producer?.kind === 'arrayIndex') {
219
+ identity = {
220
+ kind: 'arrayIndex',
221
+ array: canonicalValueIdentity(producer.array, context),
222
+ index: canonicalValueIdentity(producer.index, context),
223
+ }
224
+ } else {
225
+ identity = {kind: 'local', owner: context.identityOwner, value}
179
226
  }
180
- return `v:${context.identityNamespace}${value}`
227
+ context.identityByValue[value] = identity
228
+ return identity
181
229
  }
182
230
 
183
231
  export function sameRuntimeValue(left: ValueID, right: ValueID, context: ExpressionContext): boolean {
184
- return left === right || canonicalValueKey(left, context) === canonicalValueKey(right, context)
232
+ return left === right || sameValueIdentity(
233
+ canonicalValueIdentity(left, context),
234
+ canonicalValueIdentity(right, context),
235
+ )
185
236
  }
186
237
 
187
238
  export function addPrecondition(preconditions: InferredPrecondition[], candidate: InferredPrecondition): void {