INTERFACE ASTWalk; (***************************************************************************) (* Copyright (C) Olivetti 1989 *) (* All Rights reserved *) (* *) (* Use and copy of this software and preparation of derivative works based *) (* upon this software are permitted to any person, provided this same *) (* copyright notice and the following Olivetti warranty disclaimer are *) (* included in any copy of the software or any modification thereof or *) (* derivative work therefrom made by any person. *) (* *) (* This software is made available AS IS and Olivetti disclaims all *) (* warranties with respect to this software, whether expressed or implied *) (* under any law, including all implied warranties of merchantibility and *) (* fitness for any purpose. In no event shall Olivetti be liable for any *) (* damages whatsoever resulting from loss of use, data or profits or *) (* otherwise arising out of or in connection with the use or performance *) (* of this software. *) (***************************************************************************) IMPORT AST; (* This module supports a walk of an AST in a standard way, calling a user-supplied procedure at each node. The sub-nodes of a given node are visited in the 'natural' order, i.e. as one would read the corresponding program text. The caller can choose whether the callback procedure is called on entry to the node, exit from the node, or both. The walk may be aborted at any time. Recursive walks are supported. In order to support the passing of arguments/results to/from the callback procedure, the callback is specified as an object type, with the callback as a method. *) EXCEPTION Aborted; TYPE VisitMode = {Entry, Exit}; VisitModeControl = SET OF VisitMode; Closure <: Closure_public; (* may be subtyped by client to hold state. *) Closure_public = OBJECT METHODS callback(n: AST.NODE; vm := VisitMode.Entry) RAISES ANY; init(): Closure; END; (* Create with "NEW(Closure, callback := YourCallback).init() *) NodeCallbackProc = PROCEDURE(n: AST.NODE) RAISES ANY; CONST OnEntry = VisitModeControl{VisitMode.Entry}; OnExit = VisitModeControl{VisitMode.Exit}; OnEntryAndExit = VisitModeControl{VisitMode.Entry, VisitMode.Exit}; PROCEDURE VisitNodes(n: AST.NODE; vc: Closure) RAISES ANY; (* Starting at 'n', the procedure bound to 'vc' is called on entry to each node. This provides a simple top-down walk, visiting each node once. *) PROCEDURE ModeVisitNodes(n: AST.NODE; vc: Closure; vm: VisitModeControl) RAISES ANY; (* If 'vm = OnEntry', this is equivalent to 'VisitNodes'; 'vm = OnExit' provides a bottom-up visit of the tree; 'vm = OnEntryAndExit' calls 'vp' on entry and after all the sub-nodes have been visited - useful for counting maximum nesting depths, for example. *) PROCEDURE IgnoreChildren(vc: Closure) RAISES {}; (* Can be called from an entry visit to suppress visiting the children of this node. Once control moves to another node, the suppression is disabled. *) (* Aborting walks *) PROCEDURE Abort() RAISES {Aborted}; (* The current walk is aborted by raising the 'Aborted' exception, which is caught by 'VisitNodes/ModeVisitNodes', thus control returns to caller of that procedure. *) PROCEDURE NodeProcClosure(p: NodeCallbackProc): Closure RAISES {}; (* Creates a closure object, with 'callback = p', which is useful for tree walks without state which do not even need the closure or entry mode. This introduces an extra level of XSindirection so using 'NodeProcClosure' is not recommended unless the callback proc is called by routines other than the tree walker. In this case forcing the other routines to give useless closure and visit mode arguments seems ugly so using 'NodeProcClosure' may be appropriate *) END ASTWalk.