with Ada.Text_IO, Ada.Float_Text_IO; use Ada.Text_IO, Ada.Float_Text_IO; procedure rpncalc is generic type T is private; package stack is type stack is limited private; procedure push( s : in out stack; e : in T); -- push an item onto the stack procedure pop( s : in out stack; e : out T); -- pop an item off the stack procedure top( s : in stack; e : out T; n : out boolean); -- show (without popping) the top element of a stack -- a boolean is returned indicating if the stack is empty function length( s : in stack) return natural; -- Return the length of a specified stack private type stack_element; type stack is access stack_element; type stack_element is record val : T; next : stack := null; end record; end stack; package body stack is procedure push( s : in out stack; e : in T) is new_element : stack; begin new_element := new stack_element; new_element.all.val := e; new_element.all.next := s; s := new_element; end push; procedure pop( s : in out stack; e : out T) is begin e := s.all.val; s := s.all.next; end pop; procedure top( s : in stack; e : out T; n : out boolean) is begin if s /= null then e := s.all.val; n := false; else n := true; end if; end top; function length( s : in stack) return natural is c : natural := 0; tmp : stack := s; begin while tmp /= null loop c := c + 1; tmp := tmp.all.next; end loop; return c; end length; end stack; -- Instantiate float version of stack package var_stack is new stack(T => float); use var_stack; -- Variable declerations vars : var_stack.stack; -- stack of floats var : float; -- current operand l,r,a : float; -- temp floats for operations ch : character; -- current character eol : boolean; -- end of line empty : boolean; -- is the stack empty empty_stack : exception; -- raise if stack is empty unrec_input : exception; -- raise on unrecognised input div_by_zero : exception; -- raise if user tries to divide by 0 num_error : exception; -- raise if user gives '_' not followed by a num begin while not end_of_file loop while not end_of_line loop INPUT: begin look_ahead(ch, eol); while ch = ' ' loop -- eat whitespace get(ch); look_ahead(ch, eol); end loop; look_ahead(ch, eol); if ch = '+' then get(ch); if length(vars) >= 2 then pop(vars, r); pop(vars, l); push(vars, l + r); else raise empty_stack; end if; elsif ch = '-' then get(ch); if length(vars) >= 2 then pop(vars, r); pop(vars, l); push(vars, l - r); else raise empty_stack; end if; elsif ch = '/' then get(ch); if length(vars) >= 2 then top(vars, r, empty); if r = 0.0 then raise div_by_zero; else pop(vars, r); end if; pop(vars, l); push(vars, l / r); else raise empty_stack; end if; elsif ch = '*' then get(ch); if length(vars) >= 2 then pop(vars, r); pop(vars, l); push(vars, l * r); else raise empty_stack; end if; elsif (ch >= '0' and ch <= '9') or ch = '_' then if ch = '_' then get(ch); look_ahead(ch, eol); if ch >= '0' and ch <= '9' then get(var); var := -var; else raise num_error; end if; else get(var); end if; push(vars, var); else raise unrec_input; end if; exception when empty_stack => put_line("Warning: not enough operands! Skipping operator..."); when unrec_input => put_line("Warning: unrecognised operator! skipping..."); when div_by_zero => put_line("Warning: You asked to divide by zero! Ignoring."); when num_error => put_line("Warning: Expecting a number after '_'!"); end input; end loop; top(vars, a, empty); if not empty then put(a); new_line; end if; look_ahead(ch, eol); if eol then skip_line; end if; end loop; pop(vars, a); put(a); new_line; end rpncalc;