This appendix gives the final versions of the packages developed in this book. There are minor differences between the forms of the packages shown here and those in the main text; apart from some changes in layout, all with clauses are now shown in full and use and use type clauses are sometimes placed differently. In some cases, use clauses that were assumed for the sake of clarity of exposition in the main text are omitted in favour of fully qualified names. These changes do not affect the meaning or the behaviour of the code.
(See chapter 4)
package JE is -- an empty package! end JE;
with JE.Times; use JE.Times; package JE.Appointments is type Appointment_Type is abstract tagged private; function Date (Appt : Appointment_Type) return Time_Type; function Details (Appt : Appointment_Type) return String; procedure Appointment (Date : in Time_Type; Details : in String; Result : out Appointment_Type); procedure Put (Appt : in Appointment_Type) is abstract; private type Appointment_Type is abstract tagged record Time : Time_Type; Details : String (1..50); Length : Natural := 0; end record; end JE.Appointments; ----------------- Package body ----------------- package body JE.Appointments is function Date (Appt : Appointment_Type) return Time_Type is begin return Appt.Time; end Date; function Details (Appt : Appointment_Type) return String is begin return Appt.Details (1..Appt.Length); end Details; procedure Appointment (Date : in Time_Type; Details : in String; Result : out Appointment_Type) is begin Result.Time := Date; if Details'Length > Result.Details'Length then Result.Details := Details(Details'First .. Details'First+Result.Details'Length-1); Result.Length := Result.Details'Length; else Result.Details(1..Details'Length) := Details; Result.Length := Details'Length; end if; end Appointment; end JE.Appointments;
package JE.Appointments.Meetings is subtype Room_Type is Integer range 100 .. 999; type Meeting_Type is abstract new Appointment_Type with private; procedure Meeting (Date : in Time_Type; Details : in String; Room : in Room_Type; Result : out Meeting_Type); function Room (Appt : Meeting_Type) return Room_Type; -- Date, Details and Put inherited unchanged from Appointment_Type; -- so is Appointment, but don't use it! private type Meeting_Type is abstract new Appointment_Type with record Room : Room_Type; end record; end JE.Appointments.Meetings; ----------------- Package body ----------------- package body JE.Appointments.Meetings is procedure Meeting (Date : in Time_Type; Details : in String; Room : in Room_Type; Result : out Meeting_Type) is begin Appointment (Date, Details, Result); Result.Room := Room; end Meeting; function Room (Appt : Meeting_Type) return Room_Type is begin return Appt.Room; end Room; end JE.Appointments.Meetings;
(See chapter 15)
package JE.Appointments.Deadlines is type Deadline_Type is abstract new Appointment_Type with null record; procedure Put (Appt : in Deadline_Type) is abstract; -- Date, Details and Appointment inherited unchanged -- from Appointment_Type end JE.Appointments.Deadlines;
(See chapters 10, 11, 12 and 15)
with JE.Appointments, JE.Lists; use JE.Appointments; package JE.Diaries is type Diary_Type is limited private; procedure Load (Diary : in out Diary_Type; From : in String); procedure Save (Diary : in Diary_Type; To : in String); procedure Add (Diary : in out Diary_Type; Appt : in Appointment_Type'Class); function Choose (Diary : Diary_Type; Appt : Positive) return Appointment_Type'Class; procedure Delete (Diary : in out Diary_Type; Appt : in Positive); function Size (Diary : Diary_Type) return Natural; Diary_Error : exception; private type Appointment_Access is access Appointment_Type'Class; package Lists is new JE.Lists (Item_Type => Appointment_Access); type Diary_Type is limited record List : Lists.List_Type; end record; end JE.Diaries; ----------------- Package body ----------------- with Ada.Streams.Stream_IO, JE.Times; package body JE.Diaries is use type JE.Times.Time_Type; -- to allow use of ">" use type Lists.List_Iterator; -- to allow use of "/=" function Size (Diary : Diary_Type) return Natural is begin return Lists.Size(Diary.List); end Size; function Choose (Diary : Diary_Type; Appt : Positive) return Appointment_Type'Class is Iterator : Lists.List_Iterator; begin if Appt not in 1 .. Lists.Size(Diary.List) then raise Diary_Error; else Iterator := Lists.First(Diary.List); for I in 2 .. Appt loop Iterator := Lists.Succ(Iterator); end loop; return Lists.Value(Iterator).all; end if; end Choose; procedure Delete (Diary : in out Diary_Type; Appt : in Positive) is Iterator : Lists.List_Iterator; begin if Appt not in 1 .. Lists.Size(Diary.List) then raise Diary_Error; else Iterator := Lists.First(Diary.List); for I in 2 .. Appt loop Iterator := Lists.Succ(Iterator); end loop; Lists.Delete (Iterator); end if; end Delete; procedure Add (Diary : in out Diary_Type; Appt : in Appointment_Type'Class) is Iterator : Lists.List_Iterator; begin Iterator := Lists.First(Diary.List); while Iterator /= Lists.Last(Diary.List) loop exit when Date(Lists.Value(Iterator).all) > Date(Appt); Iterator := Lists.Succ(Iterator); end loop; Lists.Insert (Iterator, new Appointment_Type'Class'(Appt)); exception when Storage_Error => raise Diary_Error; end Add; procedure Save (Diary : in Diary_Type; To : in String) is File : Ada.Streams.Stream_IO.File_Type; Stream : Ada.Streams.Stream_IO.Stream_Access; I : Lists.List_Iterator := Lists.First(Diary.List); begin Ada.Streams.Stream_IO.Create (File, Name => To); Stream := Ada.Streams.Stream_IO.Stream(File); while I /= Lists.Last(Diary.List) loop Appointment_Type'Class'Output (Stream, Lists.Value(I).all); I := Lists.Succ(I); end loop; Ada.Streams.Stream_IO.Close (File); end Save; procedure Load (Diary : in out JE.Diaries.Diary_Type; From : in String) is File : Ada.Streams.Stream_IO.File_Type; Stream : Ada.Streams.Stream_IO.Stream_Access; begin while Size(Diary) > 0 loop Lists.Delete (Lists.First(Diary.List)); end loop; Ada.Streams.Stream_IO.Open (File, Name => From, Mode => Ada.Streams.Stream_IO.In_File); Stream := Ada.Streams.Stream_IO.Stream(File); while not Ada.Streams.Stream_IO.End_Of_File(File) loop Add (Diary, Appointment_Type'Class'Input (Stream)); end loop; Ada.Streams.Stream_IO.Close (File); exception when Ada.Streams.Stream_IO.Name_Error => raise Diary_Error; end Load; end JE.Diaries;
(See chapter 17)
with JE.Pointers; package JE.Expressions is type Expression_Type is tagged limited private; function Evaluate (Syntax : Expression_Type; Expr : String) return Integer; Syntax_Error : exception; private type Priority_Type is range 0..9; subtype Operator_Priority_Type is Priority_Type range 1..Priority_Type'Last; type Expression_Type is tagged limited null record; type Token_Type is abstract tagged null record; type Token_Access is access Token_Type'Class; package Token_Pointers is new JE.Pointers (Token_Type'Class, Token_Access); subtype Token_Pointer is Token_Pointers.Pointer_Type; procedure Next_Token (Syntax : in Expression_Type; Expr : in String; From : in out Positive; Token : in out Token_Pointer); procedure Fetch_Token (Syntax : in Expression_Type; Expr : in String; From : in out Positive; Token : in out Token_Pointer); procedure Parse (Syntax : in Expression_Type; Expr : in String; From : in out Positive; Prio : in Priority_Type; Result : out Integer; Next : in out Token_Pointer); procedure Get_Operand (Syntax : in Expression_Type; Expr : in String; From : in out Positive; Result : out Integer; Next : in out Token_Pointer); type Left_Parenthesis is new Token_Type with null record; type Right_Parenthesis is new Token_Type with null record; type End_Of_Expression is new Token_Type with null record; type Operand_Type is abstract new Token_Type with null record; function Value (Operand : Operand_Type) return Integer is abstract; type Number_Type (Value : Integer) is new Operand_Type with null record; function Value (Operand : Number_Type) return Integer; type Operator_Type is abstract new Token_Type with null record; function Priority (Operator : Operator_Type) return Operator_Priority_Type is abstract; type Unary_Operator_Type is abstract new Operator_Type with null record; function Apply (Operator : Unary_Operator_Type; Right : Integer) return Integer is abstract; type Binary_Operator_Type is abstract new Operator_Type with null record; function Apply (Operator : Binary_Operator_Type; Left, Right : Integer) return Integer is abstract; type Variadic_Operator_Type is abstract new Binary_Operator_Type with null record; function Apply (Operator : Variadic_Operator_Type; Right : Integer) return Integer is abstract; type Multiplying_Operator_Type is abstract new Binary_Operator_Type with null record; function Priority (Operator : Multiplying_Operator_Type) return Operator_Priority_Type; type Adding_Operator_Type is abstract new Variadic_Operator_Type with null record; function Priority (Operator : Adding_Operator_Type) return Operator_Priority_Type; function Unary_Priority (Operator : Adding_Operator_Type) return Operator_Priority_Type; type Times_Operator is new Multiplying_Operator_Type with null record; function Apply (Operator : Times_Operator; Left, Right : Integer) return Integer; type Over_Operator is new Multiplying_Operator_Type with null record; function Apply (Operator : Over_Operator; Left, Right : Integer) return Integer; type Plus_Operator is new Adding_Operator_Type with null record; function Apply (Operator : Plus_Operator; Left, Right : Integer) return Integer; function Apply (Operator : Plus_Operator; Right : Integer) return Integer; type Minus_Operator is new Adding_Operator_Type with null record; function Apply (Operator : Minus_Operator; Left, Right : Integer) return Integer; function Apply (Operator : Minus_Operator; Right : Integer) return Integer; end JE.Expressions; ----------------- Package body ----------------- with Ada.Exceptions, Ada.Integer_Text_IO; package body JE.Expressions is use Token_Pointers; function Evaluate (Syntax : Expression_Type; Expr : String) return Integer is Token : Token_Pointer; From : Positive := Expr'First; Result : Integer; begin Parse (Expression_Type'Class(Syntax), Expr, From, Priority_Type'Last, Result, Token); if Value(Token).all not in End_Of_Expression'Class then Ada.Exceptions.Raise_Exception (Syntax_Error'Identity, "Missing operator or left parenthesis"); end if; return Result; end Evaluate; function Priority (Operator : Multiplying_Operator_Type) return Operator_Priority_Type is begin return 5; end Priority; function Priority (Operator : Adding_Operator_Type) return Operator_Priority_Type is begin return 6; end Priority; function Unary_Priority (Operator : Adding_Operator_Type) return Operator_Priority_Type is begin return 2; end Unary_Priority; function Apply (Operator : Times_Operator; Left, Right : Integer) return Integer is begin return Left * Right; end Apply; function Apply (Operator : Over_Operator; Left, Right : Integer) return Integer is begin return Left / Right; end Apply; function Apply (Operator : Plus_Operator; Left, Right : Integer) return Integer is begin return Left + Right; end Apply; function Apply (Operator : Plus_Operator; Right : Integer) return Integer is begin return Right; end Apply; function Apply (Operator : Minus_Operator; Left, Right : Integer) return Integer is begin return Left - Right; end Apply; function Apply (Operator : Minus_Operator; Right : Integer) return Integer is begin return -Right; end Apply; function Value (Operand : Number_Type) return Integer is begin return Operand.Value; end Value; procedure Next_Token (Syntax : in Expression_Type; Expr : in String; From : in out Positive; Token : in out Token_Pointer) is begin -- Find start of next token while From <= Expr'Last and then Expr(From) = ' ' loop From := From + 1; end loop; -- Check for end of expression if From > Expr'Last then Token := Pointer(new End_Of_Expression); else Fetch_Token (Expression_Type'Class(Syntax), Expr, From, Token); end if; end Next_Token; procedure Fetch_Token (Syntax : in Expression_Type; Expr : in String; From : in out Positive; Token : in out Token_Pointer) is begin case Expr(From) is when '+' => Token := Pointer(new Plus_Operator); when '-' => Token := Pointer(new Minus_Operator); when '*' => Token := Pointer(new Times_Operator); when '/' => Token := Pointer(new Over_Operator); when '(' => Token := Pointer(new Left_Parenthesis); when ')' => Token := Pointer(new Right_Parenthesis); when '0'..'9' => declare Value : Integer; begin Ada.Integer_Text_IO.Get (Expr(From..Expr'Last), Value, From); Token := Pointer(new Number_Type(Value)); end; when others => Ada.Exceptions.Raise_Exception (Syntax_Error'Identity, "Illegal character '" & Expr(From) & "'"); end case; From := From + 1; end Fetch_Token; procedure Parse (Syntax : in Expression_Type; Expr : in String; From : in out Positive; Prio : in Priority_Type; Result : out Integer; Next : in out Token_Pointer) is begin if Prio = Priority_Type'First then Get_Operand (Expression_Type'Class(Syntax), Expr, From, Result, Next); else declare Right : Integer; Op : Token_Pointer; begin Parse (Syntax, Expr, From, Prio-1, Result, Op); while Value(Op).all in Binary_Operator_Type'Class and then Priority(Binary_Operator_Type'Class(Value(Op).all)) = Prio loop Parse (Syntax, Expr, From, Prio-1, Right, Next); Result := Apply (Binary_Operator_Type'Class(Value(Op).all), Result, Right); Op := Next; end loop; Next := Op; end; end if; end Parse; procedure Get_Operand (Syntax : in Expression_Type; Expr : in String; From : in out Positive; Result : out Integer; Next : in out Token_Pointer) is Op : Token_Pointer; begin Next_Token (Expression_Type'Class(Syntax), Expr, From, Next); if Value(Next).all in Operand_Type'Class then Result := Value (Operand_Type'Class(Value(Next).all)); Next_Token (Expression_Type'Class(Syntax), Expr, From, Next); elsif Value(Next).all in Left_Parenthesis'Class then Parse (Expression_Type'Class(Syntax), Expr, From, Priority_Type'Last, Result, Next); if Value(Next).all in Right_Parenthesis'Class then Next_Token (Expression_Type'Class(Syntax), Expr, From, Next); else Ada.Exceptions.Raise_Exception (Syntax_Error'Identity, "Missing right parenthesis"); end if; elsif Value(Next).all in Unary_Operator_Type'Class then Op := Next; Parse (Expression_Type'Class(Syntax), Expr, From, Priority (Unary_Operator_Type'Class(Value(Op).all)), Result, Next); Result := Apply (Unary_Operator_Type'Class(Value(Op).all), Result); elsif Value(Next).all in Variadic_Operator_Type'Class then Op := Next; Parse (Expression_Type'Class(Syntax), Expr, From, Priority (Variadic_Operator_Type'Class(Value(Op).all)), Result, Next); Result := Apply (Variadic_Operator_Type'Class(Value(Op).all), Result); elsif Value(Next).all in End_Of_Expression'Class then Ada.Exceptions.Raise_Exception (Syntax_Error'Identity, "Expression incomplete"); else Ada.Exceptions.Raise_Exception (Syntax_Error'Identity, "Illegal token"); end if; end Get_Operand; end JE.Expressions;
(See chapter 18)
with JE.Spreadsheets; use JE.Spreadsheets; package JE.Expressions.Spreadsheet is type Formula_Type (Sheet : access Spreadsheet_Type'Class) is new Expression_Type with private; private type Cell_Operand_Type (Cell : Cell_Access) is new Operand_Type with null record; function Value (Operand : Cell_Operand_Type) return Integer; type Formula_Type (Sheet : access Spreadsheet_Type'Class) is new Expression_Type with null record; procedure Fetch_Token (Syntax : in Formula_Type; Expr : in String; From : in out Positive; Token : in out Token_Pointer); end JE.Expressions.Spreadsheet; ----------------- Package body ----------------- with JE.Spreadsheets; use JE.Spreadsheets; package body JE.Expressions.Spreadsheet is use JE.Expressions.Token_Pointers; function Value (Operand : Cell_Operand_Type) return Integer is begin if Operand.Cell = null then raise Undefined_Cell_Error; else Evaluate (Operand.Cell.all); end if; return Num_Value (Operand.Cell.all); end Value; procedure Fetch_Token (Syntax : in Formula_Type; Expr : in String; From : in out Positive; Token : in out Token_Pointer) is begin case Expr(From) is when 'A'..'Z' | 'a'..'z' => declare First : Integer := From; Cell_Ptr : Cell_Access; begin while (From <= Expr'Length) and then (Expr(From) in 'A'..'Z' or Expr(From) in 'a'..'z' or Expr(From) in '0'..'9') loop From := From + 1; end loop; Cell_Ptr := Cell (Syntax.Sheet.all, Expr(First..From-1)); Token := Pointer(new Cell_Operand_Type(Cell_Ptr)); end; when others => Fetch_Token(Expression_Type(Syntax), Expr, From, Token); end case; end Fetch_Token; end JE.Expressions.Spreadsheet;
with Ada.Finalization; use Ada.Finalization; generic type Item_Type is private; package JE.Lists is type List_Type is new Limited_Controlled with private; type List_Iterator is private; function Size (List : List_Type) return Natural; function First (List : List_Type) return List_Iterator; function Last (List : List_Type) return List_Iterator; function Succ (Iterator : List_Iterator) return List_Iterator; function Pred (Iterator : List_Iterator) return List_Iterator; function Value (Iterator : List_Iterator) return Item_Type; procedure Insert (Iterator : in List_Iterator; Item : in Item_Type); procedure Delete (Iterator : in List_Iterator); List_Error : exception; private type Item_Record; type Item_Access is access Item_Record; type Item_Record is record Item : Item_Type; Next : Item_Access; Pred : Item_Access; end record; type List_Header is record First : Item_Access; Last : Item_Access; Count : Natural := 0; end record; type List_Access is access List_Header; type List_Type is new Limited_Controlled with record List : List_Access := new List_Header; end record; procedure Finalize (Object : in out List_Type); type List_Iterator is record List : List_Access; Current : Item_Access; end record; end JE.Lists; ----------------- Package body ----------------- with Ada.Unchecked_Deallocation; package body JE.Lists is procedure Delete_Item is new Ada.Unchecked_Deallocation (Item_Record, Item_Access); function Size (List : List_Type) return Natural is begin return List.List.Count; end Size; function First (List : List_Type) return List_Iterator is begin return (List => List.List, Current => List.List.First); end First; function Last (List : List_Type) return List_Iterator is begin return (List => List.List, Current => null); end Last; function Succ (Iterator : List_Iterator) return List_Iterator is begin if Iterator.List = null or else Iterator.Current = null then raise List_Error; else return (List => Iterator.List, Current => Iterator.Current.Next); end if; end Succ; function Pred (Iterator : List_Iterator) return List_Iterator is begin if Iterator.List = null or else Iterator.Current = Iterator.List.First then raise List_Error; elsif Iterator.Current = null then return (List => Iterator.List, Current => Iterator.List.Last); else return (List => Iterator.List, Current => Iterator.Current.Pred); end if; end Pred; function Value (Iterator : List_Iterator) return Item_Type is begin if Iterator.List = null or else Iterator.Current = null then raise List_Error; else return Iterator.Current.Item; end if; end Value; procedure Delete (Iterator : in List_Iterator) is Item : Item_Access := Iterator.Current; begin if Iterator.List = null or else Iterator.Current = null then raise List_Error; else if Iterator.Current.Next = null then Iterator.List.Last := Iterator.Current.Pred; else Iterator.Current.Next.Pred := Iterator.Current.Pred; end if; if Iterator.Current.Pred = null then Iterator.List.First := Iterator.Current.Next; else Iterator.Current.Pred.Next := Iterator.Current.Next; end if; Delete_Item (Item); Iterator.List.Count := Iterator.List.Count - 1; end if; end Delete; procedure Insert (Iterator : in List_Iterator; Item : in Item_Type) is New_Item : Item_Access; begin if Iterator.List = null then raise List_Error; else New_Item := new Item_Record; New_Item.Next := Iterator.Current; New_Item.Item := Item; if Iterator.Current = null then New_Item.Pred := Iterator.List.Last; Iterator.List.Last := New_Item; else New_Item.Pred := Iterator.Current.Pred; Iterator.Current.Pred := New_Item; end if; if Iterator.Current = Iterator.List.First then Iterator.List.First := New_Item; else New_Item.Pred.Next := New_Item; end if; Iterator.List.Count := Iterator.List.Count + 1; end if; end Insert; procedure Finalize (Object : in out List_Type) is procedure Delete_Header is new Ada.Unchecked_Deallocation (List_Header, List_Access); begin while First(Object) /= Last(Object) loop Delete (First(Object)); end loop; Delete_Header (Object.List); end Finalize; end JE.Lists;
(See chapter 12)
with JE.Lists; generic package JE.Menus is type Action_Type is access procedure; type Menu_Type is limited private; procedure Add (Menu : in out Menu_Type; Title : in String; Key : in Character; Action : in Action_Type); function Execute (Menu : Menu_Type) return Boolean; private type Menu_Item_Type is record Title : String (1..40); Length : Natural; Choice : Character; Action : Action_Type; end record; package Menu_Lists is new JE.Lists (Menu_Item_Type); type Menu_Type is limited record Menu_List : Menu_Lists.List_Type; end record; end JE.Menus; ----------------- Package body ----------------- with Ada.Text_IO, Ada.Characters.Handling; use Ada.Text_IO; package body JE.Menus is procedure Add (Menu : in out Menu_Type; Title : in String; Key : in Character; Action : in Action_Type) is Item : Menu_Item_Type; use Menu_Lists; begin if Title'Length > Item.Title'Length then Item.Title := Title (Title'First .. Item.Title'Length-Title'First+1); Item.Length := Item.Title'Length; else Item.Title (Item.Title'First .. Title'Length-Item.Title'First+1) := Title; Item.Length := Title'Length; end if; Item.Choice := Ada.Characters.Handling.To_Upper(Key); Item.Action := Action; Insert( Last(Menu.Menu_List), Item ); end Add; function Execute (Menu : Menu_Type) return Boolean is Item : Menu_Item_Type; Choice : Character; use Menu_Lists; I : List_Iterator; begin loop New_Line (3); -- Display the menu I := First(Menu.Menu_List); while I /= Last(Menu.Menu_List) loop Item := Value(I); Put (" ["); Put (Item.Choice); Put ("] "); Put_Line (Item.Title(1..Item.Length)); I := Succ(I); end loop; -- Display the Quit option and prompt Put_Line (" [Q] Quit"); Put ("Enter your choice: "); -- Get user's choice in upper case Get (Choice); Choice := Ada.Characters.Handling.To_Upper(Choice); if Choice = 'Q' then -- Quit chosen, so return return False; else -- Search menu for choice I := First(Menu.Menu_List); while I /= Last(Menu.Menu_List) loop if Choice = Value(I).Choice then -- Choice found, so call procedure and return Value(I).Action.all; return True; end if; I := Succ(I); end loop; end if; -- Choice wasn't found, so display error message and loop Put_Line ("Invalid choice -- please try again."); end loop; end Execute; end JE.Menus;
(See chapter 16)
with Ada.Finalization; generic type Item_Type(<>) is limited private; type Access_Type is access Item_Type; package JE.Pointers is type Pointer_Type is private; function Pointer (Value : Access_Type) return Pointer_Type; function Value (Pointer : Pointer_Type) return Access_Type; private type Reference_Counted_Object is record Value : Access_Type; Count : Natural; end record; type Reference_Counted_Pointer is access Reference_Counted_Object; type Pointer_Type is new Ada.Finalization.Controlled with record Pointer : Reference_Counted_Pointer; end record; procedure Finalize (Object : in out Pointer_Type); procedure Adjust (Object : in out Pointer_Type); end JE.Pointers; ----------------- Package body ----------------- with Ada.Unchecked_Deallocation; package body JE.Pointers is procedure Delete_Item is new Ada.Unchecked_Deallocation (Item_Type, Access_Type); procedure Delete_Pointer is new Ada.Unchecked_Deallocation (Reference_Counted_Object, Reference_Counted_Pointer); function Pointer (Value : Access_Type) return Pointer_Type is Object : Pointer_Type; begin if Object.Pointer /= null then Delete_Item (Object.Pointer.Value); else Object.Pointer := new Reference_Counted_Object; end if; Object.Pointer.all := (Value => Value, Count => 1); return Object; end Pointer; function Value (Pointer : Pointer_Type) return Access_Type is begin if Pointer.Pointer = null then return null; else return Pointer.Pointer.Value; end if; end Value; procedure Finalize (Object : in out Pointer_Type) is begin if Object.Pointer /= null then Object.Pointer.Count := Object.Pointer.Count - 1; if Object.Pointer.Count = 0 then Delete_Item (Object.Pointer.Value); Delete_Pointer (Object.Pointer); end if; end if; end Finalize; procedure Adjust (Object : in out Pointer_Type) is begin if Object.Pointer /= null then Object.Pointer.Count := Object.Pointer.Count + 1; end if; end Adjust; end JE.Pointers;
(See chapter 18)
with Ada.Finalization, Ada.Exceptions, JE.Lists, JE.Pointers; use Ada.Finalization; package JE.Spreadsheets is type Spreadsheet_Type is abstract tagged limited private; type Cell_Type (Sheet : access Spreadsheet_Type'Class) is abstract tagged limited private; type Formula_Cell_Type (Sheet : access Spreadsheet_Type'Class; Size : Natural) is new Cell_Type(Sheet) with private; type String_Cell_Type (Sheet : access Spreadsheet_Type'Class; Size : Natural) is new Cell_Type(Sheet) with private; type Cell_Access is access Cell_Type'Class; function Formula_Cell (Sheet : access Spreadsheet_Type; Value : String) return Cell_Access; function String_Cell (Sheet : access Spreadsheet_Type; Value : String) return Cell_Access; procedure Evaluate (Cell : in out Cell_Type) is abstract; function Text_Value (Cell : Cell_Type) return String is abstract; function Contents (Cell : Cell_Type) return String is abstract; function Num_Value (Cell : Cell_Type) return Integer is abstract; procedure Recalculate (Sheet : in out Spreadsheet_Type); procedure Display (Sheet : in out Spreadsheet_Type) is abstract; procedure Change (Sheet : in out Spreadsheet_Type); function Changed (Sheet : Spreadsheet_Type) return Boolean; function Cell (Sheet : Spreadsheet_Type; Where : String) return Cell_Access; procedure Delete (Sheet : in out Spreadsheet_Type; Where : in String); procedure Insert (Sheet : in out Spreadsheet_Type; Where : in String; What : in Cell_Access); Cell_Name_Length : constant := 6; Circularity_Error : exception; Undefined_Cell_Error : exception; private type Cell_State_Type is (Unknown, Defined, Undefined, Evaluating, Error); type Evaluation_Number is mod 2; type Cell_Type (Sheet : access Spreadsheet_Type'Class) is abstract new Limited_Controlled with record State : Cell_State_Type := Unknown; Eval : Evaluation_Number; end record; type Formula_Cell_Type (Sheet : access Spreadsheet_Type'Class; Size : Natural) is new Cell_Type(Sheet) with record Text : String(1..Size); Value : Integer; end record; procedure Evaluate (Cell : in out Formula_Cell_Type); function Text_Value (Cell : Formula_Cell_Type) return String; function Contents (Cell : Formula_Cell_Type) return String; function Num_Value (Cell : Formula_Cell_Type) return Integer; type String_Cell_Type (Sheet : access Spreadsheet_Type'Class; Size : Natural) is new Cell_Type(Sheet) with record Text : String(1..Size); end record; procedure Evaluate (Cell : in out String_Cell_Type); function Text_Value (Cell : String_Cell_Type) return String; function Contents (Cell : String_Cell_Type) return String; function Num_Value (Cell : String_Cell_Type) return Integer; subtype Cell_Size is Natural range 0 .. Cell_Name_Length; package Cell_Pointers is new JE.Pointers (Cell_Type'Class, Cell_Access); type Cell_Record is record Where : String (1..Cell_Name_Length); Size : Cell_Size; Cell : Cell_Pointers.Pointer_Type; end record; package Cell_Lists is new JE.Lists (Cell_Record); type Spreadsheet_Type is abstract tagged limited record Cells : Cell_Lists.List_Type; Dirty : Boolean := False; Eval : Evaluation_Number := Evaluation_Number'First; end record; procedure Updated (Sheet : in out Spreadsheet_Type); procedure Handle_Error (Sheet : access Spreadsheet_Type; Error : in Ada.Exceptions.Exception_Occurrence); function Evaluation (Sheet : Spreadsheet_Type) return Evaluation_Number; end JE.Spreadsheets; ----------------- Package body ----------------- with JE.Expressions.Spreadsheet, Ada.Characters.Handling; use JE.Expressions.Spreadsheet, Ada.Characters.Handling; package body JE.Spreadsheets is use Cell_Lists, Cell_Pointers; function Formula_Cell (Sheet : access Spreadsheet_Type'Class; Value : String) return Cell_Access is Cell : Cell_Access := new Formula_Cell_Type(Sheet, Value'Length); begin Formula_Cell_Type(Cell.all).Text := Value; return Cell; end Formula_Cell; function String_Cell (Sheet : access Spreadsheet_Type; Value : String) return Cell_Access is Cell : Cell_Access := new String_Cell_Type(Sheet, Value'Length); begin String_Cell_Type(Cell.all).Text := Value; return Cell; end String_Cell; procedure Recalculate (Sheet : in out Spreadsheet_Type) is Iter : Cell_Lists.List_Iterator; Cell : Cell_Pointers.Pointer_Type; begin Sheet.Eval := Sheet.Eval + 1; -- increment evaluation number if Changed(Spreadsheet_Type'Class(Sheet)) then Iter := First(Sheet.Cells); while Iter /= Last(Sheet.Cells) loop Cell := Value(Iter).Cell; Evaluate (Value(Cell).all); Iter := Succ(Iter); end loop; Updated (Spreadsheet_Type'Class(Sheet)); end if; end Recalculate; procedure Change (Sheet : in out Spreadsheet_Type) is begin Sheet.Dirty := True; end Change; procedure Updated (Sheet : in out Spreadsheet_Type) is begin Sheet.Dirty := False; end Updated; function Changed (Sheet : Spreadsheet_Type) return Boolean is begin return Sheet.Dirty; end Changed; function Cell (Sheet : Spreadsheet_Type; Where : String) return Cell_Access is Iter : Cell_Lists.List_Iterator := Cell_Lists.First(Sheet.Cells); Cell : Cell_Record; begin while Iter /= Cell_Lists.Last(Sheet.Cells) loop Cell := Cell_Lists.Value(Iter); exit when To_Upper(Cell.Where(1..Cell.Size)) = To_Upper(Where); Iter := Cell_Lists.Succ(Iter); end loop; if Iter /= Cell_Lists.Last(Sheet.Cells) then return Value(Cell_Lists.Value(Iter).Cell); else return null; end if; end Cell; procedure Delete (Sheet : in out Spreadsheet_Type; Where : in String) is Iter : Cell_Lists.List_Iterator; Cell : Cell_Record; begin Iter := Cell_Lists.First (Sheet.Cells); while Iter /= Cell_Lists.Last (Sheet.Cells) loop Cell := Cell_Lists.Value(Iter); if To_Upper(Cell.Where(1..Cell.Size)) = To_Upper(Where) then Delete (Iter); Change (Spreadsheet_Type'Class(Sheet)); exit; end if; Iter := Cell_Lists.Succ(Iter); end loop; end Delete; procedure Insert (Sheet : in out Spreadsheet_Type; Where : in String; What : in Cell_Access) is New_Cell : Cell_Record; begin Delete (Sheet, Where); if What /= null then New_Cell.Size := Integer'Min(Cell_Name_Length,Where'Length); New_Cell.Where (1..New_Cell.Size) := Where (Where'First .. Where'First+New_Cell.Size-1); New_Cell.Cell := Pointer(What); Cell_Lists.Insert (Last(Sheet.Cells), New_Cell); end if; Change (Spreadsheet_Type'Class(Sheet)); end Insert; function Evaluation (Sheet : Spreadsheet_Type) return Evaluation_Number is begin return Sheet.Eval; end Evaluation; function Text_Value (Cell : String_Cell_Type) return String is begin return Cell.Text; end Text_Value; function Contents (Cell : String_Cell_Type) return String is begin return Cell.Text; end Contents; procedure Evaluate (Cell : in out String_Cell_Type) is begin Cell.State := Undefined; end Evaluate; function Num_Value (Cell : String_Cell_Type) return Integer is begin raise Undefined_Cell_Error; return 0; -- to keep some compilers happy! end Num_Value; function Text_Value (Cell : Formula_Cell_Type) return String is begin if Cell.State = Defined then return Integer'Image(Cell.Value); elsif Cell.State = Error then return "*ERROR*"; else return ""; end if; end Text_Value; function Contents (Cell : Formula_Cell_Type) return String is begin return Cell.Text; end Contents; function Num_Value (Cell : Formula_Cell_Type) return Integer is begin if Cell.State = Defined then return Cell.Value; else raise Undefined_Cell_Error; end if; end Num_Value; procedure Evaluate (Cell : in out Formula_Cell_Type) is Expr : Formula_Type (Cell.Sheet); begin if Cell.State = Evaluating then raise Circularity_Error; elsif Cell.State = Unknown or Cell.Eval /= Evaluation(Cell.Sheet.all) then Cell.Eval := Evaluation(Cell.Sheet.all); Cell.State := Evaluating; Cell.Value := Evaluate (Expr, Cell.Text); Cell.State := Defined; end if; exception when Undefined_Cell_Error => if Cell.State /= Error then Cell.State := Undefined; end if; when Fault : Circularity_Error | JE.Expressions.Syntax_Error | Constraint_Error => Cell.State := Error; Handle_Error (Cell.Sheet, Fault); end Evaluate; procedure Handle_Error (Sheet : access Spreadsheet_Type; Error : Ada.Exceptions.Exception_Occurrence) is begin null; -- do nothing, but allow for future overriding end Handle_Error; end JE.Spreadsheets;
(See chapter 19)
package JE.Spreadsheets.Active is use JE.Spreadsheets; type Active_Spreadsheet_Type is abstract new Spreadsheet_Type with private; procedure Change (Sheet : in out Active_Spreadsheet_Type); function Changed (Sheet : Active_Spreadsheet_Type) return Boolean; type Counting_Cell_Type (Sheet : access Spreadsheet_Type'Class) is new Cell_Type(Sheet) with private; function Counting_Cell (Sheet : access Spreadsheet_Type'Class) return Cell_Access; procedure Evaluate (Cell : in out Counting_Cell_Type); function Contents (Cell : Counting_Cell_Type) return String; function Text_Value (Cell : Counting_Cell_Type) return String; function Num_Value (Cell : Counting_Cell_Type) return Integer; private protected type Shared_Flag_Type is function State return Boolean; procedure Set; procedure Clear; private State_Flag : Boolean := False; end Shared_Flag_Type; type Active_Spreadsheet_Type is abstract new Spreadsheet_Type with record Modified : Shared_Flag_Type; end record; procedure Updated (Sheet : in out Active_Spreadsheet_Type); task type Counter_Task (Sheet : access Spreadsheet_Type'Class) is entry Get (Value : out Integer); entry Stop; end Counter_Task; type Counting_Cell_Type (Sheet : access Spreadsheet_Type'Class) is new Cell_Type(Sheet) with record Counter : Counter_Task(Sheet); end record; procedure Finalize (Object : in out Counting_Cell_Type); end JE.Spreadsheets.Active; ----------------- Package body ----------------- with Ada.Calendar; package body JE.Spreadsheets.Active is use type Ada.Calendar.Time; -- to allow use of "+" task body Counter_Task is type Count_Type is mod 10000; Count : Count_Type := Count_Type'First; Update_Time : Ada.Calendar.Time := Ada.Calendar.Clock + 5.0; begin loop select accept Get (Value : out Integer) do Value := Integer(Count); end Get; or accept Stop; exit; or delay until Update_Time; Update_Time := Update_Time + 5.0; Count := Count + 1; Change (Sheet.all); end select; end loop; end Counter_Task; protected body Shared_Flag_Type is function State return Boolean is begin return State_Flag; end State; procedure Set is begin State_Flag := True; end Set; procedure Clear is begin State_Flag := False; end Clear; end Shared_Flag_Type; procedure Change (Sheet : in out Active_Spreadsheet_Type) is begin Sheet.Modified.Set; end Change; procedure Updated (Sheet : in out Active_Spreadsheet_Type) is begin Sheet.Modified.Clear; end Updated; function Changed (Sheet : Active_Spreadsheet_Type) return Boolean is begin return Sheet.Modified.State; end Changed; procedure Finalize (Object : in out Counting_Cell_Type) is begin Object.Counter.Stop; end Finalize; function Contents (Cell : Counting_Cell_Type) return String is begin return "<5-second counter>"; end Contents; function Text_Value (Cell : Counting_Cell_Type) return String is begin return Integer'Image(Num_Value(Cell)); end Text_Value; function Num_Value (Cell : Counting_Cell_Type) return Integer is I : Integer; begin Cell.Counter.Get (I); return I; end Num_Value; procedure Evaluate (Cell : in out Counting_Cell_Type) is begin Cell.State := Defined; end Evaluate; function Counting_Cell (Sheet : access Spreadsheet_Type'Class) return Cell_Access is Cell : Cell_Access := new Counting_Cell_Type (Sheet); begin return Cell; end Counting_Cell; end JE.Spreadsheets.Active;
(See chapter 13)
with JE.Lists; generic type Item_Type is private; package JE.Stacks is type Stack_Type is limited private; procedure Push (Stack : in out Stack_Type; Item : in Item_Type); procedure Pop (Stack : in out Stack_Type; Item : out Item_Type); function Top (Stack : Stack_Type) return Item_Type; function Size (Stack : Stack_Type) return Natural; function Empty (Stack : Stack_Type) return Boolean; Stack_Overflow, Stack_Underflow : exception; private package Lists is new JE.Lists (Item_Type); type Stack_Item; type Stack_Type is access Stack_Item; end JE.Stacks; ----------------- Package body ----------------- package body JE.Stacks is type Stack_Item is record L : Lists.List_Type; end record; procedure Push (Stack : in out Stack_Type; Item : in Item_Type) is begin if Stack = null then Stack := new Stack_Item; end if; Lists.Insert (Lists.First(Stack.L), Item); exception when Storage_Error => raise Stack_Overflow; end Push; procedure Pop (Stack : in out Stack_Type; Item : out Item_Type) is begin Item := Top(Stack); Lists.Delete (Lists.First(Stack.L)); exception when Lists.List_Error => raise Stack_Underflow; end Pop; function Top (Stack : Stack_Type) return Item_Type is begin return Lists.Value(Lists.First(Stack.L)); exception when Lists.List_Error => raise Stack_Underflow; end Top; function Size (Stack : Stack_Type) return Natural is begin if Stack = null then return 0; else return Lists.Size (Stack.L); end if; end Size; function Empty (Stack : Stack_Type) return Boolean is begin return Size(Stack) = 0; end Empty; end JE.Stacks;
(See chapter 9)
with Ada.Calendar; package JE.Times is subtype Time_Type is Ada.Calendar.Time; subtype Year_Type is Ada.Calendar.Year_Number; type Month_Type is (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec); subtype Day_Type is Ada.Calendar.Day_Number; subtype Hour_Type is Integer range 0..23; subtype Minute_Type is Integer range 0..59; subtype Second_Type is Integer range 0..59; subtype Day_Duration is Ada.Calendar.Day_Duration; function Clock return Ada.Calendar.Time renames Ada.Calendar.Clock; function Interval (Days : Natural := 0; Hours : Natural := 0; Minutes : Natural := 0; Seconds : Natural := 0) return Duration; function Year (Date : Ada.Calendar.Time) return Year_Type renames Ada.Calendar.Year; function Month (Date : Time_Type) return Month_Type; function Day (Date : Ada.Calendar.Time) return Day_Type renames Ada.Calendar.Day; function Hour (Date : Time_Type) return Hour_Type; function Minute (Date : Time_Type) return Minute_Type; function Second (Date : Time_Type) return Second_Type; function Time (Year : Year_Type; Month : Month_Type; Day : Day_Type; Hour : Hour_Type := 0; Minute : Minute_Type := 0; Second : Second_Type := 0) return Time_Type; function "+" (Left : Ada.Calendar.Time; Right : Duration) return Ada.Calendar.Time renames Ada.Calendar."+"; function "+" (Left : Duration; Right : Ada.Calendar.Time) return Ada.Calendar.Time renames Ada.Calendar."+"; function "-" (Left : Ada.Calendar.Time; Right : Duration) return Ada.Calendar.Time renames Ada.Calendar."-"; function "-" (Left : Ada.Calendar.Time; Right : Ada.Calendar.Time) return Duration renames Ada.Calendar."-"; function "<" (Left, Right : Ada.Calendar.Time) return Boolean renames Ada.Calendar."<"; function "<="(Left, Right : Ada.Calendar.Time) return Boolean renames Ada.Calendar."<="; function ">" (Left, Right : Ada.Calendar.Time) return Boolean renames Ada.Calendar.">"; function ">="(Left, Right : Ada.Calendar.Time) return Boolean renames Ada.Calendar.">="; Time_Error : exception renames Ada.Calendar.Time_Error; end JE.Times; ----------------- Package body ----------------- package body JE.Times is Seconds_Per_Minute : constant := 60; Minutes_Per_Hour : constant := 60; Hours_Per_Day : constant := 24; Seconds_Per_Hour : constant := Minutes_Per_Hour * Seconds_Per_Minute; Seconds_Per_Day : constant := Hours_Per_Day * Seconds_Per_Hour; type Integer_Time is range 0 .. Seconds_Per_Day; function Convert_Time (Time : Day_Duration) return Integer_Time is T : Integer_Time := Integer_Time (Time); begin return T mod Integer_Time'Last; end Convert_Time; function Interval (Days : Natural := 0; Hours : Natural := 0; Minutes : Natural := 0; Seconds : Natural := 0) return Duration is begin return Duration( (Days * Seconds_Per_Day) + (Hours * Seconds_Per_Hour) + (Minutes * Seconds_Per_Minute) + Seconds ); end Interval; function Month (Date : Ada.Calendar.Time) return Month_Type is begin return Month_Type'Val (Ada.Calendar.Month(Date) - 1); end Month; function Hour (Date : Time_Type) return Hour_Type is S : Ada.Calendar.Day_Duration := Ada.Calendar.Seconds (Date); begin return Hour_Type( Convert_Time(S) / Seconds_Per_Hour ); end Hour; function Minute (Date : Time_Type) return Minute_Type is S : Ada.Calendar.Day_Duration := Ada.Calendar.Seconds (Date); begin return Minute_Type( (Convert_Time(S) / Seconds_Per_Minute) mod Minutes_Per_Hour ); end Minute; function Second (Date : Time_Type) return Second_Type is S : Ada.Calendar.Day_Duration := Ada.Calendar.Seconds (Date); begin return Second_Type( Convert_Time(S) mod Seconds_Per_Minute ); end Second; function Time (Year : Year_Type; Month : Month_Type; Day : Day_Type; Hour : Hour_Type := 0; Minute : Minute_Type := 0; Second : Second_Type := 0) return Time_Type is Seconds : Day_Duration := Day_Duration( (Hour * Seconds_Per_Hour) + (Minute * Seconds_Per_Minute) + Second ); begin return Ada.Calendar.Time_Of (Year, Month_Type'Pos(Month) + 1, Day, Seconds); end Time; end JE.Times;
This file is part of
Ada 95: The Craft of Object-Oriented Programming
by John English.
Copyright © John
English 2000. All rights reserved.
Permission is given to redistribute this work for non-profit educational
use only, provided that all the constituent files are distributed without
change.