theory StarForth_Stack_Words imports StarForth_Base begin (* ========================================================================= POST-01: Stack Manipulation Words Mirrors: src/word_source/stack_words.c src/test_runner/modules/stack_words_test.c ======================================================================== *) (* ── DROP ( n -- ) ──────────────────────────────────────────────────────── *) definition forth_drop :: "vm_state \ vm_state" where "forth_drop vm = (case data_stack vm of [] \ set_error vm | _ # xs \ vm\data_stack := xs\)" lemma drop_normal: assumes "data_stack vm = x # xs" shows "data_stack (forth_drop vm) = xs \ vm_error (forth_drop vm) = vm_error vm" by (simp add: forth_drop_def assms) lemma drop_depth: assumes "data_stack vm = x # xs" shows "length (data_stack (forth_drop vm)) = length (data_stack vm) - 1" by (simp add: forth_drop_def assms) lemma drop_underflow: assumes "data_stack vm = []" shows "vm_error (forth_drop vm)" by (simp add: forth_drop_def set_error_def assms) (* ── DUP ( n -- n n ) ───────────────────────────────────────────────────── *) definition forth_dup :: "vm_state \ vm_state" where "forth_dup vm = (case data_stack vm of [] \ set_error vm | x # xs \ if ds_full vm then set_error vm else vm\data_stack := x # x # xs\)" lemma dup_normal: assumes "data_stack vm = x # xs" assumes "\ ds_full vm" shows "data_stack (forth_dup vm) = x # x # xs" by (simp add: forth_dup_def assms) lemma dup_underflow: assumes "data_stack vm = []" shows "vm_error (forth_dup vm)" by (simp add: forth_dup_def set_error_def assms) lemma dup_overflow: assumes "data_stack vm = x # xs" assumes "ds_full vm" shows "vm_error (forth_dup vm)" by (simp add: forth_dup_def set_error_def assms) lemma dup_increases_depth: assumes "data_stack vm = x # xs" assumes "\ ds_full vm" shows "length (data_stack (forth_dup vm)) = length (data_stack vm) + 1" by (simp add: forth_dup_def assms) (* ── ?DUP ( n -- n n | 0 ) ─────────────────────────────────────────────── *) (* Duplicates top if non-zero; leaves zero unchanged. *) definition forth_qdup :: "vm_state \ vm_state" where "forth_qdup vm = (case data_stack vm of [] \ set_error vm | x # xs \ if x = 0 then vm else if ds_full vm then set_error vm else vm\data_stack := x # x # xs\)" lemma qdup_zero: assumes "data_stack vm = 0 # xs" shows "data_stack (forth_qdup vm) = 0 # xs" by (simp add: forth_qdup_def assms) lemma qdup_zero_noop: assumes "data_stack vm = 0 # xs" shows "forth_qdup vm = vm" by (simp add: forth_qdup_def assms) lemma qdup_nonzero: assumes "data_stack vm = x # xs" assumes "x \ 0" assumes "\ ds_full vm" shows "data_stack (forth_qdup vm) = x # x # xs" by (simp add: forth_qdup_def assms) lemma qdup_underflow: assumes "data_stack vm = []" shows "vm_error (forth_qdup vm)" by (simp add: forth_qdup_def set_error_def assms) lemma qdup_overflow: assumes "data_stack vm = x # xs" assumes "x \ 0" assumes "ds_full vm" shows "vm_error (forth_qdup vm)" by (simp add: forth_qdup_def set_error_def assms) (* ── SWAP ( n1 n2 -- n2 n1 ) ───────────────────────────────────────────── *) (* Stack effect (right = TOS): n1=second, n2=TOS. In list notation (head = TOS): [n2, n1, rest] \ [n1, n2, rest] *) definition forth_swap :: "vm_state \ vm_state" where "forth_swap vm = (case data_stack vm of n2 # n1 # rest \ vm\data_stack := n1 # n2 # rest\ | _ \ set_error vm)" lemma swap_normal: assumes "data_stack vm = n2 # n1 # rest" shows "data_stack (forth_swap vm) = n1 # n2 # rest" by (simp add: forth_swap_def assms) lemma swap_depth_preserved: assumes "data_stack vm = n2 # n1 # rest" shows "length (data_stack (forth_swap vm)) = length (data_stack vm)" by (simp add: forth_swap_def assms) lemma swap_involutive: assumes "data_stack vm = n2 # n1 # rest" shows "data_stack (forth_swap (forth_swap vm)) = data_stack vm" by (simp add: forth_swap_def assms) lemma swap_underflow_nil: assumes "data_stack vm = []" shows "vm_error (forth_swap vm)" by (simp add: forth_swap_def set_error_def assms) lemma swap_underflow_one: assumes "data_stack vm = [x]" shows "vm_error (forth_swap vm)" by (simp add: forth_swap_def set_error_def assms) (* ── OVER ( n1 n2 -- n1 n2 n1 ) ───────────────────────────────────────── *) (* n2=TOS, n1=second. Copies n1 (second) to new TOS. In list: [n2, n1, rest] \ [n1, n2, n1, rest] *) definition forth_over :: "vm_state \ vm_state" where "forth_over vm = (case data_stack vm of n2 # n1 # rest \ if ds_full vm then set_error vm else vm\data_stack := n1 # n2 # n1 # rest\ | _ \ set_error vm)" lemma over_normal: assumes "data_stack vm = n2 # n1 # rest" assumes "\ ds_full vm" shows "data_stack (forth_over vm) = n1 # n2 # n1 # rest" by (simp add: forth_over_def assms) lemma over_second_preserved: assumes "data_stack vm = n2 # n1 # rest" assumes "\ ds_full vm" shows "hd (data_stack (forth_over vm)) = n1" by (simp add: forth_over_def assms) lemma over_underflow_nil: assumes "data_stack vm = []" shows "vm_error (forth_over vm)" by (simp add: forth_over_def set_error_def assms) lemma over_underflow_one: assumes "data_stack vm = [x]" shows "vm_error (forth_over vm)" by (simp add: forth_over_def set_error_def assms) lemma over_overflow: assumes "data_stack vm = n2 # n1 # rest" assumes "ds_full vm" shows "vm_error (forth_over vm)" by (simp add: forth_over_def set_error_def assms) (* ── ROT ( n1 n2 n3 -- n2 n3 n1 ) ─────────────────────────────────────── *) (* n3=TOS, n2=second, n1=third. Brings n1 (third) to TOS. In list: [n3, n2, n1, rest] \ [n1, n3, n2, rest] Verified against C: n3 = data_stack[dsp] (TOS) n2 = data_stack[dsp-1] (second) n1 = data_stack[dsp-2] (third) data_stack[dsp] = n1 (new TOS) data_stack[dsp-1] = n3 data_stack[dsp-2] = n2 *) definition forth_rot :: "vm_state \ vm_state" where "forth_rot vm = (case data_stack vm of n3 # n2 # n1 # rest \ vm\data_stack := n1 # n3 # n2 # rest\ | _ \ set_error vm)" lemma rot_normal: assumes "data_stack vm = n3 # n2 # n1 # rest" shows "data_stack (forth_rot vm) = n1 # n3 # n2 # rest" by (simp add: forth_rot_def assms) lemma rot_depth_preserved: assumes "data_stack vm = n3 # n2 # n1 # rest" shows "length (data_stack (forth_rot vm)) = length (data_stack vm)" by (simp add: forth_rot_def assms) lemma rot_underflow_nil: assumes "data_stack vm = []" shows "vm_error (forth_rot vm)" by (simp add: forth_rot_def set_error_def assms) lemma rot_underflow_one: assumes "data_stack vm = [x]" shows "vm_error (forth_rot vm)" by (simp add: forth_rot_def set_error_def assms) lemma rot_underflow_two: assumes "data_stack vm = [x, y]" shows "vm_error (forth_rot vm)" by (simp add: forth_rot_def set_error_def assms) (* ── -ROT ( n1 n2 n3 -- n3 n1 n2 ) ─────────────────────────────────────── *) (* Reverse rotation: brings TOS (n3) to third position. In list: [n3, n2, n1, rest] \ [n2, n1, n3, rest] Verified against C: data_stack[dsp] = n2 (new TOS) data_stack[dsp-1] = n1 data_stack[dsp-2] = n3 *) definition forth_nrot :: "vm_state \ vm_state" where "forth_nrot vm = (case data_stack vm of n3 # n2 # n1 # rest \ vm\data_stack := n2 # n1 # n3 # rest\ | _ \ set_error vm)" lemma nrot_normal: assumes "data_stack vm = n3 # n2 # n1 # rest" shows "data_stack (forth_nrot vm) = n2 # n1 # n3 # rest" by (simp add: forth_nrot_def assms) lemma nrot_depth_preserved: assumes "data_stack vm = n3 # n2 # n1 # rest" shows "length (data_stack (forth_nrot vm)) = length (data_stack vm)" by (simp add: forth_nrot_def assms) (* ROT and -ROT are mutual inverses on the data stack. *) lemma rot_nrot_inverse: assumes "data_stack vm = n3 # n2 # n1 # rest" shows "data_stack (forth_nrot (forth_rot vm)) = data_stack vm" by (simp add: forth_nrot_def forth_rot_def assms) lemma nrot_rot_inverse: assumes "data_stack vm = n3 # n2 # n1 # rest" shows "data_stack (forth_rot (forth_nrot vm)) = data_stack vm" by (simp add: forth_nrot_def forth_rot_def assms) lemma nrot_underflow_nil: assumes "data_stack vm = []" shows "vm_error (forth_nrot vm)" by (simp add: forth_nrot_def set_error_def assms) lemma nrot_underflow_one: assumes "data_stack vm = [x]" shows "vm_error (forth_nrot vm)" by (simp add: forth_nrot_def set_error_def assms) lemma nrot_underflow_two: assumes "data_stack vm = [x, y]" shows "vm_error (forth_nrot vm)" by (simp add: forth_nrot_def set_error_def assms) (* ── DROP \ DUP = identity ─────────────────────────────────────────────── *) lemma dup_then_drop: assumes "data_stack vm = x # xs" assumes "\ ds_full vm" assumes "\ vm_error vm" shows "data_stack (forth_drop (forth_dup vm)) = data_stack vm" by (simp add: forth_dup_def forth_drop_def assms) (* ── DEPTH ( -- n ) ─────────────────────────────────────────────────────── *) (* Pushes the current stack depth (number of items before DEPTH executes). C impl: depth = dsp + 1; data_stack[++dsp] = depth. *) definition forth_depth :: "vm_state \ vm_state" where "forth_depth vm = (if ds_full vm then set_error vm else vm\data_stack := int (length (data_stack vm)) # data_stack vm\)" lemma depth_pushes_count: assumes "\ ds_full vm" shows "hd (data_stack (forth_depth vm)) = int (length (data_stack vm))" by (simp add: forth_depth_def assms) lemma depth_rest_preserved: assumes "\ ds_full vm" shows "tl (data_stack (forth_depth vm)) = data_stack vm" by (simp add: forth_depth_def assms) lemma depth_increases_by_one: assumes "\ ds_full vm" shows "length (data_stack (forth_depth vm)) = length (data_stack vm) + 1" by (simp add: forth_depth_def assms) lemma depth_overflow: assumes "ds_full vm" shows "vm_error (forth_depth vm)" by (simp add: forth_depth_def set_error_def assms) (* ── PICK ( n -- x ) ───────────────────────────────────────────────────── *) (* Replaces TOS (n) with the element at index n in the current stack. Index 0 = TOS itself; index 1 = second item; etc. In-place replacement — depth unchanged. Verified against C: n = data_stack[dsp] (TOS, not popped) val = data_stack[dsp - n] data_stack[dsp] = val (replace TOS with val) Bounds check: n \ 0 and n < length (data_stack vm) *) definition forth_pick :: "vm_state \ vm_state" where "forth_pick vm = (case data_stack vm of [] \ set_error vm | n # xs \ if n < 0 \ nat n \ length (data_stack vm) then set_error vm else vm\data_stack := data_stack vm ! nat n # xs\)" lemma pick_normal: assumes "data_stack vm = n # xs" assumes "n \ 0" assumes "nat n < length (data_stack vm)" shows "data_stack (forth_pick vm) = data_stack vm ! nat n # xs" by (simp add: forth_pick_def assms) lemma pick_depth_unchanged: assumes "data_stack vm = n # xs" assumes "n \ 0" assumes "nat n < length (data_stack vm)" shows "length (data_stack (forth_pick vm)) = length (data_stack vm)" by (simp add: forth_pick_def assms) (* 0 PICK: replaces TOS (which is 0) with data_stack[0] = 0 — identity. *) lemma pick_zero_self: assumes "data_stack vm = 0 # xs" assumes "\ ds_full vm" shows "data_stack (forth_pick vm) = 0 # xs" by (simp add: forth_pick_def assms) (* 1 PICK: replaces TOS (1) with the element at index 1 = hd xs. *) lemma pick_one: assumes "data_stack vm = 1 # x # xs" shows "data_stack (forth_pick vm) = x # x # xs" by (simp add: forth_pick_def assms) lemma pick_underflow: assumes "data_stack vm = []" shows "vm_error (forth_pick vm)" by (simp add: forth_pick_def set_error_def assms) lemma pick_bounds_neg: assumes "data_stack vm = n # xs" assumes "n < 0" shows "vm_error (forth_pick vm)" by (simp add: forth_pick_def set_error_def assms) lemma pick_bounds_high: assumes "data_stack vm = n # xs" assumes "nat n \ length (data_stack vm)" shows "vm_error (forth_pick vm)" by (simp add: forth_pick_def set_error_def assms) (* ── ROLL ( +n -- ) ─────────────────────────────────────────────────────── *) (* Pops n, then rotates items. Special cases in C implementation: n = 0: pop n, return (stack depth decreases by 1, TOS unchanged) n = 1: pop n, return ("top item already at top") n \ 2: save item at index (depth - n) from bottom (= dsp+1-n after pop), shift items down to fill gap, place saved item on top. Net effect on depth: decreases by 1 (n is consumed, one item moved). Index convention after popping n (dsp' = dsp - 1): value = data_stack[dsp'+1-n] = data_stack[dsp-n] Items at indices (dsp-n)..(dsp-1) shift down by 1. Saved value placed at data_stack[dsp]. Final dsp unchanged (= dsp-1 after pop, but top slot reused). *) definition forth_roll :: "vm_state \ vm_state" where "forth_roll vm = (case data_stack vm of [] \ set_error vm | n # xs \ if n < 0 \ nat n > length xs then set_error vm else if n = 0 \ n = 1 then vm\data_stack := xs\ else let i = nat n item = xs ! (i - 1) rest = take (i - 1) xs @ drop i xs in vm\data_stack := item # rest\)" lemma roll_zero_nop: assumes "data_stack vm = 0 # xs" shows "data_stack (forth_roll vm) = xs" by (simp add: forth_roll_def assms) lemma roll_one_nop: assumes "data_stack vm = 1 # xs" shows "data_stack (forth_roll vm) = xs" by (simp add: forth_roll_def assms) (* 2 ROLL is equivalent to ROT (bring third item to top). *) lemma roll_two_is_rot: assumes "data_stack vm = 2 # n3 # n2 # n1 # rest" shows "data_stack (forth_roll vm) = n1 # n3 # n2 # rest" by (simp add: forth_roll_def assms) lemma roll_underflow: assumes "data_stack vm = []" shows "vm_error (forth_roll vm)" by (simp add: forth_roll_def set_error_def assms) lemma roll_bounds_neg: assumes "data_stack vm = n # xs" assumes "n < 0" shows "vm_error (forth_roll vm)" by (simp add: forth_roll_def set_error_def assms) lemma roll_bounds_high: assumes "data_stack vm = n # xs" assumes "nat n > length xs" shows "vm_error (forth_roll vm)" by (simp add: forth_roll_def set_error_def assms) end