package tiny_languages
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
Small languages from scratch: Scheme, Lisp, Smalltalk-80, Pascal, BASIC, JavaScript, HTML, CSS and more
Install
dune-project
Dependency
Authors
Maintainers
Sources
0.3.6.tar.gz
md5=7c636383d146d30ac6f2fa234a6253c8
sha512=c79f3823c5f8f57e5038eb640d487c61168b84aa07c61999d6622ef9fd0c890e2b03b4c6a7cdbbe9352a49e25dda00ac7bb14693cee8e3d7beeed251351a2af0
doc/src/tiny_languages.smalltalk/St_debug.ml.html
Source file St_debug.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210(* Claude Code * * Copyright (C) 2026 Yoann Padioleau * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License * (LGPL) as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. *) (* See St_debug.mli *) module M = St_memory module B = St_bytecode module C = St_class module I = St_interp type oop = M.oop type frame = { ctx : oop; label : string; source : string; highlight : (int * int) option } (*****************************************************************************) (* Reading the stack *) (*****************************************************************************) let dead (vm : I.vm) (ctx : oop) : bool = M.fetch (I.memory vm) ctx I.c_ip = M.nil let label (vm : I.vm) (ctx : oop) : string = let m = I.memory vm in let home = I.context_home vm ctx in let meth = M.fetch m home I.c_method in let rcls = M.class_of m (M.fetch m home I.c_receiver) in let mcls = B.method_class m meth in let sel = M.string_of m (B.selector m meth) in let base = if mcls = rcls || mcls = M.nil then C.name m rcls ^ ">>" ^ sel else C.name m rcls ^ "(" ^ C.name m mcls ^ ")>>" ^ sel in if I.is_block_context vm ctx || I.is_closure_context vm ctx then "[] in " ^ base else base (* the send in progress: at the top of a stepped process, the one about * to be made; elsewhere, the last one made *) let highlight (vm : I.vm) (ctx : oop) ~(next : bool) : (int * int) option = let m = I.memory vm in let ip = M.fetch m ctx I.c_ip in if ip = M.nil then None else let ip = M.int_of ip in let map = B.pcmap m (I.context_method vm ctx) in let pick = if next then List.find_opt (fun (pc, _, _) -> pc >= ip) map else List.fold_left (fun acc ((pc, _, _) as e) -> if pc < ip then Some e else acc) None map in match pick with Some (_, a, b) when b > a -> Some (a, b) | _ -> None let frames ?(stepping = false) (vm : I.vm) (p : I.process) : frame list = let m = I.memory vm in let rec go ctx top acc = if ctx = M.nil || List.length acc > 200 then List.rev acc else let f = { ctx; label = label vm ctx; source = B.source m (I.context_method vm ctx); highlight = highlight vm ctx ~next:(top && stepping); } in go (M.fetch m ctx I.c_sender) false (f :: acc) in go p.top true [] let variables (vm : I.vm) (ctx : oop) : (string * oop) list = let m = I.memory vm in let home = I.context_home vm ctx in let names = B.temp_names m (M.fetch m home I.c_method) in let a = M.fields m home in ("self", a.(I.c_receiver)) :: List.mapi (fun i n -> (n, if I.c_temps + i < Array.length a then a.(I.c_temps + i) else M.nil)) names |> List.filter (fun (n, _) -> n <> "" && n.[0] <> ' ') let fields (vm : I.vm) (o : oop) : (string * oop) list = let m = I.memory vm in if M.is_int o then [] else let k = M.known m in let cls = M.class_of m o in let names = C.inst_var_names m cls in let limit = 200 in match M.body m o with | M.Pointers a -> let n = List.length names in List.mapi (fun i name -> (name, a.(i))) (List.filteri (fun i _ -> i < Array.length a) names) @ List.init (min limit (max 0 (Array.length a - n))) (fun i -> (string_of_int (i + 1), a.(n + i))) | M.Bytes b -> let string = cls = k.string || cls = k.symbol in List.init (min limit (Bytes.length b)) (fun i -> let c = Char.code (Bytes.get b i) in (string_of_int (i + 1), if string then k.characters.(c) else M.of_int c)) | M.Method (a, _) -> Array.sub a 0 (Array.length a - B.trailer_size) |> Array.to_list |> List.mapi (fun i v -> ((if i = 0 then "header" else "literal " ^ string_of_int i), v)) | M.Float _ | M.Free -> [] (*****************************************************************************) (* Moving on *) (*****************************************************************************) let budget = 20_000_000 let run_until (vm : I.vm) (p : I.process) (cond : I.vm -> bool) : unit = I.resume p; I.run vm p ~budget ~stop_when:cond; (* claude: a step that never came back, an endless loop: stopped * anyway, rather than left running under the debugger's feet *) match p.state with I.Runnable -> I.suspend p "Step: still running" | _ -> () (* stop in [ctx] before its next send once the current one is done, or * as soon as ctx has returned. The current one: in a context below the * top, the send in progress, done when ctx is active again at its ip; * at the top, the next send ahead. *) let over_condition (vm : I.vm) (p : I.process) (ctx : oop) : I.vm -> bool = let m = I.memory vm in let meth = I.context_method vm ctx in let code = B.bytecodes m meth in let pcs = List.map (fun (pc, _, _) -> pc) (B.pcmap m meth) in let ip0 = match M.fetch m ctx I.c_ip with ip when M.is_int ip -> M.int_of ip | _ -> 0 in let after = if p.top <> ctx then Some ip0 else Option.map (fun s1 -> s1 + B.length_at code s1) (List.find_opt (fun pc -> pc >= ip0) pcs) in match after with | None -> fun vm -> dead vm ctx | Some after -> let passed = ref false in fun vm -> if dead vm ctx then true else if I.active_context vm = ctx then begin let ip = I.ip vm in if ip = after then passed := true; !passed && List.mem ip pcs end else false let step (vm : I.vm) (p : I.process) (ctx : oop) : unit = if not (dead vm ctx) then run_until vm p (over_condition vm p ctx) let step_into (vm : I.vm) (p : I.process) (ctx : oop) : unit = if not (dead vm ctx) then begin let over = over_condition vm p ctx in let m = I.memory vm in run_until vm p (fun vm -> let a = I.active_context vm in (a <> ctx && M.fetch m a I.c_sender = ctx) || over vm) end let restart (vm : I.vm) (p : I.process) (ctx : oop) : bool = let m = I.memory vm in let ctx = I.context_home vm ctx in if dead vm ctx then false else begin let old = M.fetch m ctx I.c_method in let cls = B.method_class m old in let meth = if cls = M.nil then old else Option.value (C.local_method m cls (B.selector m old)) ~default:old in let h = B.header m meth in let size = I.c_temps + h.frame_size in if M.size m ctx < size then begin (* a bigger context, and every reference to the old one follows *) let a = Array.make size M.nil in Array.blit (M.fields m ctx) 0 a 0 (min size (M.size m ctx)); let bigger = M.alloc m ~cls:(M.known m).method_context (M.Pointers a) in M.become m ctx bigger end; let a = M.fields m ctx in a.(I.c_method) <- meth; a.(I.c_ip) <- M.of_int 0; a.(I.c_sp) <- M.of_int (I.c_temps + h.num_temps - 1); for i = I.c_temps + h.num_args to Array.length a - 1 do a.(i) <- M.nil done; p.top <- ctx; I.flush_cache vm; true end let proceed (p : I.process) : unit = I.resume p let not_understood (vm : I.vm) (p : I.process) : (oop * string * oop) option = let m = I.memory vm in let top = p.top in if top = M.nil || I.is_block_context vm top then None else let meth = M.fetch m top I.c_method in if M.string_of m (B.selector m meth) <> "doesNotUnderstand:" then None else let msg = M.fetch m top I.c_temps in let receiver = M.fetch m top I.c_receiver in let sender = M.fetch m top I.c_sender in if M.is_int msg || M.class_of m msg <> (M.known m).message then None else Some (M.class_of m receiver, M.string_of m (M.fetch m msg 0), sender) let template (selector : string) : string = let n = St_ast.arity selector in let pattern = if n = 0 then selector else if not (String.contains selector ':') then selector ^ " anObject" else String.split_on_char ':' selector |> List.filter (( <> ) "") |> List.mapi (fun i k -> k ^ ": arg" ^ string_of_int (i + 1)) |> String.concat " " in pattern ^ "\n\t\"A method for " ^ selector ^ ", to write here.\"\n\t^self"
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>