| (* SymbolKey.mod binary tree operations for storing symbols. |
| |
| Copyright (C) 2001-2025 Free Software Foundation, Inc. |
| Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>. |
| |
| This file is part of GNU Modula-2. |
| |
| GNU Modula-2 is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GNU Modula-2 is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Modula-2; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. *) |
| |
| IMPLEMENTATION MODULE SymbolKey ; |
| |
| |
| FROM Storage IMPORT ALLOCATE, DEALLOCATE ; |
| FROM StrIO IMPORT WriteString, WriteLn ; |
| FROM NumberIO IMPORT WriteCard ; |
| FROM NameKey IMPORT WriteKey ; |
| FROM Assertion IMPORT Assert ; |
| FROM Debug IMPORT Halt ; |
| |
| |
| TYPE |
| SymbolTree = POINTER TO Node ; |
| Node = RECORD |
| KeyName : Name ; (* The sorted entity *) |
| KeySym : WORD ; (* The value entity *) |
| Left : SymbolTree ; |
| Right : SymbolTree ; |
| END ; |
| |
| |
| PROCEDURE InitTree (VAR t: SymbolTree) ; |
| BEGIN |
| NEW(t) ; |
| WITH t^ DO |
| Left := NIL ; |
| Right := NIL |
| END |
| END InitTree ; |
| |
| |
| (* |
| we used to get problems compiling KillTree below - so it was split |
| into the two procedures below. |
| |
| |
| PROCEDURE KillTree (VAR t: SymbolTree) ; |
| BEGIN |
| IF t#NIL |
| THEN |
| Kill(t) ; (* Would like to place Kill in here but the compiler *) |
| (* gives a type incompatible error... so i've split *) |
| (* the procedure into two. - Problem i think with *) |
| (* VAR t at the top? *) |
| t := NIL |
| END |
| END KillTree ; |
| |
| |
| PROCEDURE Kill (t: SymbolTree) ; |
| BEGIN |
| IF t#NIL |
| THEN |
| Kill(t^.Left) ; |
| Kill(t^.Right) ; |
| DISPOSE(t) |
| END |
| END Kill ; |
| *) |
| |
| |
| PROCEDURE KillTree (VAR t: SymbolTree) ; |
| BEGIN |
| IF t#NIL |
| THEN |
| KillTree(t^.Left) ; |
| KillTree(t^.Right) ; |
| DISPOSE(t) ; |
| t := NIL |
| END |
| END KillTree ; |
| |
| |
| (* |
| ContainsSymKey - return TRUE if tree, t, contains an entry for, NameKey. |
| *) |
| |
| PROCEDURE ContainsSymKey (t: SymbolTree; NameKey: Name) : BOOLEAN ; |
| VAR |
| father, |
| child : SymbolTree ; |
| BEGIN |
| FindNodeParentInTree(t, NameKey, child, father) ; |
| RETURN child#NIL |
| END ContainsSymKey ; |
| |
| |
| PROCEDURE GetSymKey (t: SymbolTree; NameKey: Name) : WORD ; |
| VAR |
| father, |
| child : SymbolTree ; |
| BEGIN |
| FindNodeParentInTree(t, NameKey, child, father) ; |
| IF child=NIL |
| THEN |
| RETURN NulKey |
| ELSE |
| RETURN child^.KeySym |
| END |
| END GetSymKey ; |
| |
| |
| PROCEDURE PutSymKey (t: SymbolTree; NameKey: Name; SymKey: WORD) ; |
| VAR |
| father, |
| child : SymbolTree ; |
| BEGIN |
| FindNodeParentInTree(t, NameKey, child, father) ; |
| IF child=NIL |
| THEN |
| (* no child found, now is NameKey less than father or greater? *) |
| IF father=t |
| THEN |
| (* empty tree, add it to the left branch of t *) |
| NEW(child) ; |
| father^.Left := child |
| ELSE |
| IF NameKey<father^.KeyName |
| THEN |
| NEW(child) ; |
| father^.Left := child |
| ELSIF NameKey>father^.KeyName |
| THEN |
| NEW(child) ; |
| father^.Right := child |
| END |
| END ; |
| WITH child^ DO |
| Right := NIL ; |
| Left := NIL ; |
| KeySym := SymKey ; |
| KeyName := NameKey |
| END |
| ELSE |
| Halt('symbol already stored', __FILE__, __FUNCTION__, __LINE__) |
| END |
| END PutSymKey ; |
| |
| |
| (* |
| DelSymKey - deletes an entry in the binary tree. |
| |
| NB in order for this to work we must ensure that the InitTree sets |
| both Left and Right to NIL. |
| *) |
| |
| PROCEDURE DelSymKey (t: SymbolTree; NameKey: Name) ; |
| VAR |
| i, child, father: SymbolTree ; |
| BEGIN |
| FindNodeParentInTree(t, NameKey, child, father) ; (* find father and child of the node *) |
| IF (child#NIL) AND (child^.KeyName=NameKey) |
| THEN |
| (* Have found the node to be deleted *) |
| IF father^.Right=child |
| THEN |
| (* Node is child and this is greater than the father. *) |
| (* Greater being on the right. *) |
| (* Connect child^.Left onto the father^.Right. *) |
| (* Connect child^.Right onto the end of the right *) |
| (* most branch of child^.Left. *) |
| IF child^.Left#NIL |
| THEN |
| (* Scan for Right most node of child^.Left *) |
| i := child^.Left ; |
| WHILE i^.Right#NIL DO |
| i := i^.Right |
| END ; |
| i^.Right := child^.Right ; |
| father^.Right := child^.Left |
| ELSE |
| (* No child^.Left node therefore link over child *) |
| (* (as in a single linked list) to child^.Right *) |
| father^.Right := child^.Right |
| END ; |
| DISPOSE(child) |
| ELSE |
| (* Assert that father^.Left=child will always be true *) |
| (* Perform exactly the mirror image of the above code *) |
| |
| (* Connect child^.Right onto the father^.Left. *) |
| (* Connect child^.Left onto the end of the Left most *) |
| (* branch of child^.Right *) |
| IF child^.Right#NIL |
| THEN |
| (* Scan for Left most node of child^.Right *) |
| i := child^.Right ; |
| WHILE i^.Left#NIL DO |
| i := i^.Left |
| END ; |
| i^.Left := child^.Left ; |
| father^.Left := child^.Right |
| ELSE |
| (* No child^.Right node therefore link over c *) |
| (* (as in a single linked list) to child^.Left. *) |
| father^.Left := child^.Left |
| END ; |
| DISPOSE(child) |
| END |
| ELSE |
| Halt('trying to delete a symbol that is not in the tree - the compiler never expects this to occur', |
| __FILE__, __FUNCTION__, __LINE__) |
| END |
| END DelSymKey ; |
| |
| |
| (* |
| FindNodeParentInTree - find a node, child, in a binary tree, t, with name equal to n. |
| if an entry is found, parent is set to the node above child. |
| *) |
| |
| PROCEDURE FindNodeParentInTree (t: SymbolTree; n: Name; |
| VAR child, parent: SymbolTree) ; |
| BEGIN |
| (* remember to skip the sentinal value and assign parent and child *) |
| parent := t ; |
| IF t=NIL |
| THEN |
| Halt('parameter t should never be NIL', |
| __FILE__, __FUNCTION__, __LINE__) |
| END ; |
| Assert (t^.Right = NIL) ; |
| child := t^.Left ; |
| IF child#NIL |
| THEN |
| REPEAT |
| IF n<child^.KeyName |
| THEN |
| parent := child ; |
| child := child^.Left |
| ELSIF n>child^.KeyName |
| THEN |
| parent := child ; |
| child := child^.Right |
| END |
| UNTIL (child=NIL) OR (n=child^.KeyName) |
| END |
| END FindNodeParentInTree ; |
| |
| |
| (* |
| IsEmptyTree - returns true if SymbolTree, t, is empty. |
| *) |
| |
| PROCEDURE IsEmptyTree (t: SymbolTree) : BOOLEAN ; |
| BEGIN |
| RETURN t^.Left = NIL |
| END IsEmptyTree ; |
| |
| |
| (* |
| DoesTreeContainAny - returns true if SymbolTree, t, contains any |
| symbols which in turn return true when procedure, |
| P, is called with a symbol as its parameter. |
| The SymbolTree root is empty apart from the field, |
| Left, hence we need two procedures. |
| *) |
| |
| PROCEDURE DoesTreeContainAny (t: SymbolTree; P: IsSymbol) : BOOLEAN ; |
| BEGIN |
| RETURN SearchForAny (t^.Left, P) |
| END DoesTreeContainAny ; |
| |
| |
| (* |
| SearchForAny - performs the search required for DoesTreeContainAny. |
| The root node always contains a nul data value, |
| therefore we must skip over it. |
| *) |
| |
| PROCEDURE SearchForAny (t: SymbolTree; P: IsSymbol) : BOOLEAN ; |
| BEGIN |
| IF t=NIL |
| THEN |
| RETURN FALSE |
| ELSE |
| RETURN( P (t^.KeySym) OR |
| SearchForAny (t^.Left, P) OR |
| SearchForAny(t^.Right, P) |
| ) |
| END |
| END SearchForAny ; |
| |
| |
| (* |
| ForeachNodeDo - for each node in SymbolTree, t, a procedure, P, |
| is called with the node symbol as its parameter. |
| The tree root node only contains a legal Left pointer, |
| therefore we need two procedures to examine this tree. |
| *) |
| |
| PROCEDURE ForeachNodeDo (t: SymbolTree; P: PerformOperation) ; |
| BEGIN |
| SearchAndDo (t^.Left, P) |
| END ForeachNodeDo ; |
| |
| |
| (* |
| SearchAndDo - searches all the nodes in SymbolTree, t, and |
| calls procedure, P, with a node as its parameter. |
| It traverse the tree in order. |
| *) |
| |
| PROCEDURE SearchAndDo (t: SymbolTree; P: PerformOperation) ; |
| BEGIN |
| IF t#NIL |
| THEN |
| WITH t^ DO |
| SearchAndDo (Right, P) ; |
| P (KeySym) ; |
| SearchAndDo (Left, P) |
| END |
| END |
| END SearchAndDo ; |
| |
| |
| (* |
| CountNodes - wrapper for NoOfNodes. |
| *) |
| |
| PROCEDURE CountNodes (t: SymbolTree; condition: IsSymbol; count: CARDINAL) : CARDINAL ; |
| BEGIN |
| IF t # NIL |
| THEN |
| WITH t^ DO |
| IF condition (KeySym) |
| THEN |
| INC (count) |
| END ; |
| count := CountNodes (Left, condition, count) ; |
| count := CountNodes (Right, condition, count) |
| END |
| END ; |
| RETURN count |
| END CountNodes ; |
| |
| |
| (* |
| NoOfNodes - returns the number of nodes in the tree t. |
| *) |
| |
| PROCEDURE NoOfNodes (t: SymbolTree; condition: IsSymbol) : CARDINAL ; |
| BEGIN |
| RETURN CountNodes (t^.Left, condition, 0) |
| END NoOfNodes ; |
| |
| |
| (* |
| SearchConditional - wrapper for ForeachNodeConditionDo. |
| *) |
| |
| PROCEDURE SearchConditional (t: SymbolTree; condition: IsSymbol; P: PerformOperation) ; |
| BEGIN |
| IF t#NIL |
| THEN |
| WITH t^ DO |
| SearchConditional (Right, condition, P) ; |
| IF (KeySym # 0) AND condition (KeySym) |
| THEN |
| P (KeySym) |
| END ; |
| SearchConditional (Left, condition, P) |
| END |
| END |
| END SearchConditional ; |
| |
| |
| (* |
| ForeachNodeConditionDo - traverse the tree t and for any node which satisfied |
| condition call P. |
| *) |
| |
| PROCEDURE ForeachNodeConditionDo (t: SymbolTree; |
| condition: IsSymbol; |
| P: PerformOperation) ; |
| BEGIN |
| IF t#NIL |
| THEN |
| WITH t^ DO |
| Assert (Right = NIL) ; |
| SearchConditional (Left, condition, P) |
| END |
| END |
| END ForeachNodeConditionDo ; |
| |
| |
| END SymbolKey. |