1 What is display_source ? -------------------------- display_source is an Asis application we used for Traverse_Element and Asis testing. It's an application that can be used too as a basis for applications that imply a modification of the sources. It's based on the generic procedure Traverse_Element and the various funtionalities provided by display_source correspond to different actions on each element of the source traversed. The functionalities are : -n basis function that simply outputs the kind of each element, eventualy its subkinds and its image ( for literals ) -s evolved function that whose output is an Ada source semanticaly equivalent to the initial source, the goal here is to approach as close as possible the aspect of the initial source. The interest is that it processes every node in the program ( using for that all possible non-semantic Asis queries ), and it allows to check that the result is correct by compiling it. -i an even more evolved function that does the same thing that -s options, but in a totaly different way. The algorithm is much more simple and the goal is not to test Asis or Traverse_Element, but to be a basis structure for an Asis application. For example an application that would output html pages for a corresponding set of Ada sources, could use such a structure ( but it requires semantic queries ... ). Other example : a pretty printer ... The cool thing here is that the lexical elements are directly taken from the initial source text, and so the output is much closer to the initial source. There is also derived functions which are not indicated to the ( common ) user, but are accessable : -l is like -n but uses Asis.Text stuff to display line numbers, lengths and spans for each element. This options led to create a new option because it displays a lot of stuff that is not always required ( if you only want to see the nodes .. ) -t is like -n, but it calls two procedures, Pre_Test_Control and Post_Test_Control at the end of Pre_Node and Post_Node. Those functions change the Control and allow the testing of the Traverse_Element Control features. It was designed especialy for that purpose, and i don't see any other use ... -sl option has been suppressed -e is like -i but it is an example of what can be done with that kind of application. It turns pragmas into put_line statements. In a real application having user defined pragmas and using Asis as a Pre-Compiler can be an interesting possibility. Of course, many other things can be imagined. 2 Algorithm used for the option -s ---------------------------------- Such a functionality needs an enormous case with all the possible cases ( something like 300 different elements ). In each case we need to type the action needed to obtain the expected outputs. So, with such an amount of case, we were very interested in having something simple to program the cases. Basing our thought on that principle, the complexity of the problem was put out of the case, in several procedures designed for the 'source displayer'. 2.1 Pre_Source guide -------------------- ---------------------------------------------------------------- -- -- -- Pre_Source user's guide : -- -- -- -- In this function, you'll use 3 procedures : -- -- - Send ( String ) that will send immediatly the -- -- string passed in argument. -- -- - Push [( String, [ List_Kind, [Number of elements]] )] -- -- which means : when you have passed Number of elements -- -- elements, print the String ... The List_Kind says if -- -- the program needs to print paranthesis or separator, -- -- (see the array Separator). -- -- - Indent [ ( Number_Of_Space ) ] , when you use this -- -- procedure after a Push, it means that you want the -- -- corresponding element(s) to have one more indentation -- -- unit. -- -- - Count ( Asis.Element_List ), He he, this a very basic -- -- function that returns the number of elements in a list -- -- - Is_Here ( Asis.Element ), another basic one that -- -- returns a boolean True : Element is realy An_Element -- -- False : Element is Not_An_Element -- -- -- -- Of course you can still use any Asis function needed, for -- -- instance to determine the number of element in a list. -- -- -- ---------------------------------------------------------------- Which will give for a varible declaration : ( A : Integer := 1 ; or A : Integer ; ) when A_Variable_Declaration => -- we're waiting for the name of the variable, then we write ":" -- the trait_string here could output the reserved word "constant" -- if it appeared here. Push (": " & Trait_String ) ; -- If there is an initial value, we push a ":=", it means that -- we wait for the variable type and then we write a ":=" if Is_Here ( Asis.Declarations.Initial_Value ( Element )) then Push (":= ") ; end if ; -- we push the ";" which indicates the end of the declaration -- in fact if there is an initial value, we wait this value -- before writing the ";". If there is no initial value, -- we wait the type of the variable and we write the ";". -- This shows well that the important thing is not the kind -- of the expected elements, but the number of expected elements. Push (";" & ASCII.CR) ; Several elements of a list can be specified, for example : when An_Enumeration_Type_Definition => Push ("", Is_Comma_List, Count ( Asis.Definitions.Enumeration_Literal_Declarations ( Element ))) ; for somthing like : ( Red, Blue, Green ) Is_Comma_List indicates what is the list separator, and precises too if the list requires parenthesis or not. In the example the list a of a parenthesized kind. Moreover we see here a use of the count function which says how many elements the list contains. 2.2 Algorithm ------------- Like it is suggested by the name of the Push function, a stack is used to handle the display_source process. Lexical nodes are pushed on the stack. Then they are poped by the procedure Pass_Element which is called in the begining of Pre_Source at each element. Pass_Element decreases the number of elements in the upper lexical nodes, and pops this node if there is no element remaining in the list. It's this procedure again that displays separators and parenthesis when handling a list. On another side, in order to let the user push his nodes in the logical order ( that is lexical order ), they are pushed on a temporary stack and then poured in the main lexical_stack. This is made by the procedure Commit, called at the end of Pre_Source. The Send function is called when we want to display an element immediately, without waiting another element ( ex : we do a Send ("procedure") when arriving on a A_Procedure_Declaration element ) This is the structure of a lexical node and the corresponding explanations : type Lexical_Node is record -- Lexem is a string that the program will display after having finished -- to process the current lexical node. Lexem : Ada.Strings.Unbounded.Unbounded_String := Ada.Strings.Unbounded.Null_Unbounded_String ; -- This is the kind of the list we are currently in. -- use Not_In_A_List if you have a single element. List_Kind : List_Kinds := Not_In_A_List ; -- This is the number of elements that remain to be processed in the list -- single elements are represented by a Number_Of_Elements equal to 1 Number_Of_Elements : natural := 1 ; --------------------------------------------------------------------------- -- The boolean First_Passed is true when the first element of a list -- has been passed ( used to know if the separarator and the parenthesis -- is to be displayed ) -- cannot be set or read First_Passed : Boolean := False ; -- Indentation is the number of space to be put after a return. -- This is the real indentation that will be used for the string -- contained in Lexem. -- cannot be set or read Indentation : Natural := 0 ; -- This is the indentation reference for children. -- This will become the new Indentation for childs element -- of this node. That is because Pass_Element turns this -- value into the Current_Indentation_Reference. -- It is set by the Indent procedure. -- cannot be read Indentation_Reference : Natural := 0 ; -- No_Space is used to specify that one's mustn't print a space -- before a selector for instance ( A.B or A'First ... ) -- It is set by the No_Space procedure -- cannot be read No_Space : Boolean := False ; -- Return_list is used to specify that we should return after each -- element of a list ... -- It is set by procedure Check_If_Return_Separator -- cannot be read Return_List : Boolean := False ; -- The problem in function calls is that the things don't appear in the order -- they should be displayed, so we must have this flag ... -- It is used to make the difference between -- 1 + 2 and "+" (1, 2) -- It is set by Infix procedure. -- And read by Is_Infix function. Infixed_Operator : Boolean := False ; end record ; Lexical nodes can be considered as recursive information, on another side there is also global information in the State parameter of Traverse_Element : type Info_Source is record -- Default_Indentation_Element is the default number of spaces to add -- when you use the Indent function ( note that Indent also accepts -- an optional parameter telling the number of spaces needed ) Default_Indentation_Element : Natural := 2 ; -- When a list element sizes more than this number of -- space there a return between each element. (Not Implemented) Max_Size_Of_List_Elem_Before_Return : Natural := 10 ; -- Declaration of the stacks needed ... Lexical_Stack, Tmp_Stack : Node_Stack.Stack := Node_Stack.Empty_Stack ; -- That is the reference for the current traversed element. -- it means that this is the value pushed by the function push. -- In Pass_Element, this value is reset with the Indentation_Reference -- of the Upper lexical node. Current_Indentation_Reference : Natural := 0 ; -- Last_Commented_Line is a counter that helps displaying the -- comments. It says that all comments from line 1 to Last_Commented_Line -- have already been displayed. 0 means that no line was displayed. -- It is a number of line in the original file. Last_Commented_Line : Natural := 0 ; -- Horizontal_Position and Vertical_Position are the current -- positions in the generated file. Horizontal_Position : Natural := 0 ; Vertical_Position : Natural := 1 ; -- Is a limit to avoid line too long errors... -- In fact we should not need this ... but knowing -- when to put a new_line in lists is not that easy... Max_Line_Length : Positive := 100 ; -- Last_Char_Was_Return and Last_Char_Was_Space are used -- for smart display. In fact returns and spaces are not -- writen immediately, but just before writing something -- else, so these boolean are used to keep the trace of -- the corresponding characters. Last_Char_Was_Return : Boolean := False ; Last_Char_Was_Space : Boolean := False ; -- At the end of the display source ... the stack may not be -- empty, so we need this boolean to say that we don't want -- any element to be processed but only the stack to be poured. Finishing_Traversal : Boolean := False ; end record ; 3. Algorithm used by option -i ------------------------------ The basic idea of this application is simpler than -s option's. When nothing is done in Pre_Image, the text is displayed only in Post_Image, when an element is post traversed, the program takes its span, computes the span to be displayed and displays it. The span is from the character after the end of the last displayed span to the end of the element's span. After the end the traversal, the program makes the same with the full span of the source. The fact is that this is enough to redisplay completely the source. Pre_Image is the procedure where the user defines the actions that make his particular application, look the comments there for more information. 4. Conclusion ------------- In fact from this structure, we can draw a more general idea. Using Traverse_Element turns the explicit recursity of a Abstract Syntaxic Tree into an implicit one, so the user needs to use a structure like a stack to re-establish the recursivity.How to write a new display_source like application ? ---------------------------------------------------- It is assumed here that you want to make an application on the basis of display_source -i features. We'll use the application stubs stub_trav.ads and stub_trav.adb Follow these steps : - find a short name for your application which will replace 'stub' say you choose 'name' then copy stub_trav.ads to name_trav.ads and stub_trav.adb to name_trav.adb Then replace all the strings "Stub" by strings "Name" in name_trav.ads and name_trav.adb. - now in file global_info.ads add one or more modes for your application for example : type Application is ( ..., Name_Mode, Evolved_Name_Mode ) ; In the same file add a global mode line definition : subtype Name_Modes is Application range Name_Mode .. Evolved_Name_Mode ; That's all with this file .. - Now the main thing is in display_source.adb : + add a line with Name_Trav ; use Name_Trav ; + add an instanciation of Traverse_Element : procedure Traverse_Name is new Asis.Elements.Traverse_Element (Info_Name, Pre_Name, Post_Name) ; + more generaly, when you find a comment : -- YHSTAH It means that you have to add something there. Understanding what to add would not be a great deal because in each case, there a lot of things already there, and generaly you only have to put the same changing "Stub" ( or "Image" ) to "Name" - Then your application is ready, you now gotta put in Pre_Name function what you need to perform what you want.