@@ -149,6 +149,15 @@ partial def evalApp (state : HState) (originalExpr e1 e2 : HExpr) : HState × HE
149149 -- Third application to DynamicFieldAssign - now we can evaluate
150150 -- This handles obj[ field ] = value where field is dynamic
151151 evalDynamicFieldAssign state2 objExpr fieldExpr e2'
152+ | .deferredOp "DynamicFieldAssignReturnObj" _ =>
153+ -- First application to DynamicFieldAssignReturnObj - return partially applied
154+ (state2, .app (.deferredOp "DynamicFieldAssignReturnObj" none) e2')
155+ | .app (.deferredOp "DynamicFieldAssignReturnObj" _) objExpr =>
156+ -- Second application to DynamicFieldAssignReturnObj - return partially applied
157+ (state2, .app (.app (.deferredOp "DynamicFieldAssignReturnObj" none) objExpr) e2')
158+ | .app (.app (.deferredOp "DynamicFieldAssignReturnObj" _) objExpr) fieldExpr =>
159+ -- Third application to DynamicFieldAssignReturnObj - now we can evaluate
160+ evalDynamicFieldAssignReturnObj state2 objExpr fieldExpr e2'
152161 | .deferredOp "StringFieldAccess" _ =>
153162 -- First application to StringFieldAccess - return partially applied
154163 (state2, .app (.deferredOp "StringFieldAccess" none) e2')
@@ -179,25 +188,69 @@ partial def evalApp (state : HState) (originalExpr e1 e2 : HExpr) : HState × HE
179188
180189-- Handle dynamic field access: obj[ field ] where field is dynamic
181190partial def evalDynamicFieldAccess (state : HState) (objExpr fieldExpr : HExpr) : HState × HExpr :=
182- -- First try to extract a numeric field index from the field expression
183- match extractFieldIndex fieldExpr with
184- | some fieldIndex =>
185- -- We have a numeric field index, use regular deref
186- evalHExpr state (.deref objExpr fieldIndex)
187- | none =>
188- -- Can't extract a numeric field index, return error
189- (state, .lambda (LExpr.const "error_dynamic_field_access_failed" none))
191+ let (s1, objVal) := evalHExpr state objExpr
192+ let (s2, keyVal) := evalHExpr s1 fieldExpr
193+ match objVal with
194+ | .address addr =>
195+ match keyVal with
196+ | .lambda (LExpr.const k _) =>
197+ match k.toNat? with
198+ | some n =>
199+ match s2.getField addr n with
200+ | some v => (s2, v)
201+ | none => (s2, .lambda (LExpr.const s! "error_field_{ n} _not_found" none))
202+ | none =>
203+ match s2.getStringField addr k with
204+ | some v => (s2, v)
205+ | none => (s2, .lambda (LExpr.const s! "error_string_field_{ k} _not_found" none))
206+ | _ => (s2, .lambda (LExpr.const "error_invalid_field_key" none))
207+ | .null => (s2, .lambda (LExpr.const "error_null_dereference" none))
208+ | _ => (s2, .lambda (LExpr.const "error_invalid_address" none))
190209
191210-- Handle dynamic field assignment: obj[ field ] = value where field is dynamic
192211partial def evalDynamicFieldAssign (state : HState) (objExpr fieldExpr valueExpr : HExpr) : HState × HExpr :=
193- -- First try to extract a numeric field index from the field expression
194- match extractFieldIndex fieldExpr with
195- | some fieldIndex =>
196- -- We have a numeric field index, use regular assign
197- evalHExpr state (.assign objExpr fieldIndex valueExpr)
198- | none =>
199- -- Can't extract a numeric field index, return error
200- (state, .lambda (LExpr.const "error_dynamic_field_assign_failed" none))
212+ let (s1, objVal) := evalHExpr state objExpr
213+ let (s2, keyVal) := evalHExpr s1 fieldExpr
214+ let (s3, valVal) := evalHExpr s2 valueExpr
215+ match objVal with
216+ | .address addr =>
217+ match keyVal with
218+ | .lambda (LExpr.const k _) =>
219+ match k.toNat? with
220+ | some n =>
221+ match s3.setField addr n valVal with
222+ | some s' => (s', valVal)
223+ | none => (s3, .lambda (LExpr.const s! "error_cannot_update_field_{ n} " none))
224+ | none =>
225+ match s3.setStringField addr k valVal with
226+ | some s' => (s', valVal)
227+ | none => (s3, .lambda (LExpr.const s! "error_cannot_update_string_field_{ k} " none))
228+ | _ => (s3, .lambda (LExpr.const "error_invalid_field_key" none))
229+ | .null => (s3, .lambda (LExpr.const "error_null_assignment" none))
230+ | _ => (s3, .lambda (LExpr.const "error_invalid_address_assignment" none))
231+
232+ -- Handle dynamic field assignment but return the *object address* to allow chaining
233+ partial def evalDynamicFieldAssignReturnObj (state : HState) (objExpr fieldExpr valueExpr : HExpr) : HState × HExpr :=
234+ let (s1, objVal) := evalHExpr state objExpr
235+ let (s2, keyVal) := evalHExpr s1 fieldExpr
236+ let (s3, valVal) := evalHExpr s2 valueExpr
237+ match objVal with
238+ | .address addr =>
239+ match keyVal with
240+ | .lambda (LExpr.const k _) =>
241+ match k.toNat? with
242+ | some n =>
243+ match s3.setField addr n valVal with
244+ | some s' => (s', .address addr)
245+ | none => (s3, .lambda (LExpr.const s! "error_cannot_update_field_{ n} " none))
246+ | none =>
247+ match s3.setStringField addr k valVal with
248+ | some s' => (s', .address addr)
249+ | none => (s3, .lambda (LExpr.const s! "error_cannot_update_string_field_{ k} " none))
250+ | _ => (s3, .lambda (LExpr.const "error_invalid_field_key" none))
251+ | .null => (s3, .lambda (LExpr.const "error_null_assignment" none))
252+ | _ => (s3, .lambda (LExpr.const "error_invalid_address_assignment" none))
253+
201254
202255-- Handle string field access: str.fieldName where fieldName is a string literal
203256partial def evalStringFieldAccess (state : HState) (objExpr fieldExpr : HExpr) : HState × HExpr :=
@@ -223,7 +276,6 @@ partial def evalStringFieldAccess (state : HState) (objExpr fieldExpr : HExpr) :
223276partial def extractFieldIndex (expr : HExpr) : Option Nat :=
224277 match expr with
225278 | .lambda (LExpr.const s _) =>
226- -- Try to parse the string as a natural number
227279 s.toNat?
228280 | _ => none
229281
@@ -253,6 +305,25 @@ namespace HExpr
253305def allocSimple (fields : List (Nat × HExpr)) : HExpr :=
254306 .alloc (HMonoTy.mkObj fields.length HMonoTy.int) fields
255307
308+ def dynamicAssign (obj : HExpr) (key : HExpr) (val : HExpr) : HExpr :=
309+ HExpr.app
310+ (HExpr.app
311+ (HExpr.app (HExpr.deferredOp "DynamicFieldAssign" none) obj)
312+ key)
313+ val
314+
315+ /-- Like `dynamicAssign`, but evaluates to the *object* so it can be chained. --/
316+ def dynamicAssignReturnObj (obj : HExpr) (key : HExpr) (val : HExpr) : HExpr :=
317+ HExpr.app
318+ (HExpr.app
319+ (HExpr.app (HExpr.deferredOp "DynamicFieldAssignReturnObj" none) obj)
320+ key)
321+ val
322+
323+ def dynamicAlloc (numFields : List (Nat × HExpr)) (dynFields : List (HExpr × HExpr)) : HExpr :=
324+ let obj0 := allocSimple numFields
325+ dynFields.foldl (fun acc (k, v) => dynamicAssignReturnObj acc k v) obj0
326+
256327end HExpr
257328
258329end Heap
0 commit comments