@@ -35,43 +35,43 @@ private partial def natToHexGo (n : Nat) (acc : String) : String :=
3535private def natToHex (n : Nat) : String :=
3636 if n == 0 then "0" else natToHexGo n ""
3737
38- partial def ppValue : Value → String
38+ partial def Value.pp : Value → String
3939 | .unit => "()"
4040 | .field g => toString g.val.toNat
41- | .tuple vs => "(" ++ String.intercalate ", " (vs.toList.map ppValue ) ++ ")"
42- | .array vs => "[" ++ String.intercalate ", " (vs.toList.map ppValue ) ++ "]"
41+ | .tuple vs => "(" ++ String.intercalate ", " (vs.toList.map Value.pp ) ++ ")"
42+ | .array vs => "[" ++ String.intercalate ", " (vs.toList.map Value.pp ) ++ "]"
4343 | .ctor g args =>
4444 let name := g.toName.toString
4545 if args.isEmpty then name
46- else name ++ "(" ++ String.intercalate ", " (args.toList.map ppValue ) ++ ")"
46+ else name ++ "(" ++ String.intercalate ", " (args.toList.map Value.pp ) ++ ")"
4747 | .fn g => "fn(" ++ g.toName.toString ++ ")"
4848 | .pointer _ n => "&0x" ++ natToHex n
4949
50- instance : ToString Value := ⟨ppValue ⟩
50+ instance : ToString Value := ⟨Value.pp ⟩
5151
5252/-- Pretty-print a `Value` while auto-dereferencing pointers up to `depth`
5353levels. Used by the `dbg!` interpreter helper so users see structured
5454content like `App(Const(3, []), BVar(0))` instead of opaque `&0x123`. -/
55- partial def ppValueDeref (store : Store) (depth : Nat) : Value → String
55+ partial def Value.ppDeref (store : Store) (depth : Nat) : Value → String
5656 | .unit => "()"
5757 | .field g => toString g.val.toNat
58- | .tuple vs => "(" ++ String.intercalate ", " (vs.toList.map (ppValueDeref store depth)) ++ ")"
59- | .array vs => "[" ++ String.intercalate ", " (vs.toList.map (ppValueDeref store depth)) ++ "]"
58+ | .tuple vs => "(" ++ String.intercalate ", " (vs.toList.map (Value.ppDeref store depth)) ++ ")"
59+ | .array vs => "[" ++ String.intercalate ", " (vs.toList.map (Value.ppDeref store depth)) ++ "]"
6060 | .ctor g args =>
6161 let name := g.toName.toString
6262 if args.isEmpty then name
63- else name ++ "(" ++ String.intercalate ", " (args.toList.map (ppValueDeref store depth)) ++ ")"
63+ else name ++ "(" ++ String.intercalate ", " (args.toList.map (Value.ppDeref store depth)) ++ ")"
6464 | .fn g => "fn(" ++ g.toName.toString ++ ")"
6565 | .pointer _ n =>
66- if depth == 0 then "&0x" ++ natToHex n
66+ if depth == 0 then "..."
6767 else
6868 match store.getByIdx n with
6969 | some (vs, _) =>
7070 -- Stored value is `Array Value`; for tagged enums it's
7171 -- typically `[ctor]` or `[tag, fields...]`. Recurse on each.
7272 match vs.toList with
73- | [v] => ppValueDeref store (depth - 1 ) v
74- | _ => "[" ++ String.intercalate ", " (vs.toList.map (ppValueDeref store (depth - 1 ))) ++ "]"
73+ | [v] => Value.ppDeref store (depth - 1 ) v
74+ | _ => "[" ++ String.intercalate ", " (vs.toList.map (Value.ppDeref store (depth - 1 ))) ++ "]"
7575 | none => "&0x" ++ natToHex n ++ "(unbound)"
7676
7777-- ---------------------------------------------------------------------------
@@ -130,6 +130,30 @@ instance : ToString Interrupt where
130130 s! " in { g} ({ argStr} )"
131131 msg ++ "\n Call stack:\n " ++ String.intercalate "\n " frames
132132
133+ /-- Pretty-print an `Interrupt`, auto-dereferencing pointer values up to
134+ `depth` pointer-follows, and showing at most `stackLimit` call-stack frames
135+ (starting from the innermost). -/
136+ def Interrupt.ppDeref (store : Store) (depth : Nat) (stackLimit : Nat) :
137+ Interrupt → String
138+ | .ret v => s! "unexpected return: { Value.ppDeref store depth v} "
139+ | .error msg [] => msg
140+ | .error msg stack =>
141+ let total := stack.length
142+ let shown := stack.take stackLimit
143+ let rule := String.ofList (List.replicate 80 '─' )
144+ let frames := shown.zipIdx.map fun ((g, args), i) =>
145+ let argStr := String.intercalate ", " (args.map (Value.ppDeref store depth))
146+ let header := s! "─── #{ i} " ++ g.toName.toString ++ " "
147+ let pad := String.ofList (List.replicate (max 1 (80 - header.length)) '─' )
148+ header ++ pad ++ s! "\n { g} ({ argStr} )"
149+ let trailer := if total > stackLimit
150+ then s! "\n ... ({ total - stackLimit} more frame(s) elided)"
151+ else ""
152+ msg ++ s! "\n Call stack ({ total} frames, #0 = innermost):\n "
153+ ++ String.intercalate "\n " frames
154+ ++ trailer
155+ ++ "\n " ++ rule
156+
133157-- ---------------------------------------------------------------------------
134158-- Interpreter monad
135159-- ---------------------------------------------------------------------------
@@ -139,7 +163,7 @@ structure InterpState where
139163 ioBuffer : IOBuffer
140164 callCache : Std.HashMap (Global × List Value) Value := {}
141165
142- abbrev InterpM := StateT InterpState (Except Interrupt )
166+ abbrev InterpM := ExceptT Interrupt (StateM InterpState )
143167
144168private def throwErr (msg : String) : InterpM α :=
145169 throw (.error msg [])
@@ -356,7 +380,7 @@ partial def interp (decls : Decls) (bindings : Bindings) : Term → InterpM Valu
356380 | some t =>
357381 let v ← interp decls bindings t
358382 let store ← getStore
359- dbg_trace s! "{ label} : { ppValueDeref store 16 v} "
383+ dbg_trace s! "{ label} : { Value.ppDeref store 16 v} "
360384 interp decls bindings cont
361385 | .ioGetInfo key => do
362386 let keyGs ← expectFieldArray (← interp decls bindings key)
@@ -392,15 +416,18 @@ end
392416
393417def runFunction (decls : Decls) (funcName : Global) (inputs : List Value)
394418 (ioBuffer : IOBuffer := default) :
395- Except Interrupt (Value × InterpState) := do
396- let f ← match decls.getByKey funcName with
397- | some (.function f) => pure f
398- | _ => throw (.error s! "Function not found: { funcName} " [])
399- if inputs.length != f.inputs.length then
400- throw (.error s! "runFunction: arity mismatch for { funcName} : \
401- expected { f.inputs.length} , got { inputs.length} " [])
402- let bindings := f.inputs.map (·.1 ) |>.zip inputs
403- StateT.run (interp decls bindings f.body) { ioBuffer }
419+ Except Interrupt Value × InterpState :=
420+ let init : InterpState := { ioBuffer }
421+ match decls.getByKey funcName with
422+ | some (.function f) =>
423+ if inputs.length != f.inputs.length then
424+ (.error (.error s! "runFunction: arity mismatch for { funcName} : \
425+ expected { f.inputs.length} , got { inputs.length} " []), init)
426+ else
427+ let bindings := f.inputs.map (·.1 ) |>.zip inputs
428+ StateT.run (ExceptT.run (interp decls bindings f.body)) init
429+ | _ =>
430+ (.error (.error s! "Function not found: { funcName} " []), init)
404431
405432end Aiur
406433
0 commit comments