1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
|
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.
|