Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Design

Using Action Charts


SEP88: USING ACTION CHARTS

USING ACTION CHARTS

Using action charts for software development.

Martin Stitt

Martin Stitt is a software engineer for The Software Link Inc. He is a member of the design team for PC-MOS/386 and is currently writing a book on it (to be published later this year). He can be reached at 705 Chestnut St., Eugene, OR 97404.


In my work as a software engineer, I have experimented with a variety of diagramming methods for software design and documentation and have taken a great interest in software engineering tools in general. What I have discovered is that, although software engineering tools are becoming more available in the PC world, their high price still keeps them out of the reach of many developers. In this article, I will show how you can implement a technique known as action-chart diagramming--that is, the use of brackets to illustrate a program's structure graphically--with nothing more than an editor and a set of keyboard macros. Although the tool I describe is not as comprehensive and sophisticated as a full-scale commercial package, it is quite capable and is essentially free.

A primary objective in applying software engineering tools to software design is to offload to the computer as much of the clerical and mechanical details inherent in program development as possible. The programmer's talents can then focus on actual logic design and verification. A principal technique in the implementation of a CASE toolset is diagramming, and although many different diagramming methods exist, each with their own strengths and weaknesses, I shall limit my discussion here to action charting. This method is well suited to the development of procedural logic in almost any langnage and is easy to draw on a computer.

You may already be familiar with formatting utilities that produce a printed listing of a source code module annotated with action-chart style brackets. One example is Source Print, produced by Alderbaran Laboratories. By illustrating the nesting and grouping of program statements graphically, these utilities can provide a clearer picture of what's going on m a program. Although I certainly do not wish to deny the usefulness of these aids, applying action charts after you've created a source module is like buying a road map only after you've got lost. It is possible to develop program logic using such brackets from the very beginning and not have keypad while holding down the key to waste time being "lost." And the benefits can be realized throughout all phases of the project's life cycle.

To demomstrate how you can turn an editor into an action-chart development workstation, I will describe how to set up macros in WordPerfect; you can use almost any full-screen editor or word-processing program, however, if your editor has a built-in macro facility, so much the better. If not, a ProKey-type utility should provide the needed capabilities. To produce the brackets of an action-chart within an editor, you need to use certain block-graphics characters that are part of IBM's extended character set (see the sidebar "How The Macros Work," page 22). These characters are available even on a simple monochrome display and on many popular printers as well, so you should not need special hardware. One exception I've found is that some printers will not print double-lined block characters properly (an Okidata 293 will, but the smaller Okidata 193 prints them as single-lined characters).

A little-known fact about the PC AT computer is that by holding down the Alt key and entering a decimal number from 0 to 255 on the numeric keypad, you can enter any character code, including control codes, normal alphanumeric characters, and the extended character set. The extended set ranges in value from 128 to 255 and includes foreign-language characters, block graphics, and mathematical symbols. The only exception I know of is within the PC-MOS/386 multiuser/multitasking operating system, in which you use such keystrokes to select the task currently visible at your console. In this environment, you can key an Alt-999 to disable this special use of Alt and numeric key pad keystrokes.

In addition to being well suited for editing on a PC, action charts have some distinct advantages in their own right. The illustration in Figure 1 , page 20, shows how closely the graphic constructs of action-charts parallel the logical constructs of a typical high-level language. By using brackets you can build a consistent, well-organized structure. Unlike old-fashioned flowcharts, which allow the design of scattered spaghetti-like logic, these brackets implicitly support the one-entry/one-exit rule of structured programming -- a design method that actually makes it difficult to put together ba ly structured code. About the only way to develop a poorly organized program flow is to abuse the EXIT mechanism or to concoct some other use of GOTOs.

As a means of representing procedural logic, diagramming methods offer intuitive clarity by illustrating process flow pictorially. Aside from picking up a few new keyboarding habits to work with the macros, the learning curve for action-chart development is fairly insignificant. And an additional benefit is that this clarity can result in better communication of your ideas and plans to others. When you need to prepare a design specification, whether it be soiely for your own use, to distribute to others in your programming team, or to review with clients prior to coding their application, drafting pseudo-code in action-chart format is an excellent form of documentation. Such a picture can be worth a thousand words, especially when your audience is not technically oriented.

Action-charts can also be valuable in other phases of a program's life cycle. Their value as a form of documentation should be obvious, and although updating a chart every time you modily source code is just one more thing to attend to, don't neglect it. The effort involved can more than pay for itself down the road, and it's much more practical than updating a hand-drawn chart. It is also possible to develop a program directly in an action-chart document and then filter off the brackets to obtain a compiler-ready source file. This way, the documentation is the source file, so the update problem does not exist.

Another fairly common situation in software maintenance is having to modify someone else's code. If you need a detailed understanding of a program you've never seen before, reverse charting it can greatly increase your chances of adding more new features than problems.

Decomposing a Program With Action Charts

When you've got a hot new idea for the next best-selling software package, you will probably want to start writing code immediately. Although this may feel productive, it can often end up not being so. Becoming immersed in low-level details with little or no preplanning can mean creating procedures and data structures that with a broader perspective you would have written differently.

I've found that even when it seems as though a certain program should take only about three days to write if I just jumped in and start coding, it's much better to spend the first day or two developing a complete plan and running tests on high-level models. It can feel strange to not see any tangible results for a couple of days (for example, source code that compiles and runs), but the usual result is that it takes me only a few hours to write up the code once I've developed a thorough plan. The debugging time is also significantly less than if I had neglected the higher-level planning, and the code produced tends to be much cleaner, more maintainable, and more portable.

Once you become used to such a practice and start realizing its benefits, your definition of "tangible results" will change. Seeing a well-constructed plan come together and knowing how it can improve your overall productivity can be just as grati:ying as seeing code actually execute right away.

In certain situations, however, you may find it helpful or even necessary actually to develop low-level procedures early on. A good example is a procedure that is dependent on external events-a low-level I/O driver for an operating-system or process-control application, for instance. You may have to write an initial version of such a driver to determine just what degree of control is possible and how best to implement the system. It has been said that the best approach to design is not just from the top down or bottom up but a balanced mixture of the two. Too much of either can mean a narrow perspective that will cost you in the long run.

The exact steps involved in this decomposition process will vary depending on the complexity of the project and the programmer's experience and preferred methods of analysis. Still, the two key steps are a thorough round of planning before you write code, and a disciplined approach to breaking down the program's logic. This is where action-charts are especially appropriate.

Action charts are suitable for developing all phases of a program, from high-level pseudocode down to detailed low-level logic. As mentioned earlier, you can even work down to the source-code level in action-chart format and then use a utility program to filter off the brackets, resulting in ready-to-compile code. If you develop your practice accordingly, you could make all updates to the chart version of your program, filter again, and then recompile-never actually editing the source code file itself. The filter program then becomes in effect a pre-processor which can be run from a batch file or make facility, just as any other translation process. In this way, your chart is always up to date and you're working with the code at a much higher level.

Among the steps involved in designing a program are:

    1. Gather facts about required output and input data and general program function.

    2. Set up data analysis charts and models and develop ideas about the processes to be used.

    3. Consider performance requirements, storage needs, user interface, and so on.

    4. Use screen-painting and modeling tools to set up and exercise a prototype of the user interface.

    5. Organize and refine all facts and ideas gathered in the previous steps in order to develop high-level project documentation and an initial pseudocode version of the program's logic.

    6. Review your initial pseudocode. Does it fulfill your needs? is it efficient? What processes are common and could be developed as subprocedures? How can modules be organized to allow for program growth?

    7. Make successive passes through the pseudocode, refining it until a level of detail is reached at which an implementation in the target language is evident.

My preference is to use an outline processor (I use MaxThink) to gather and organize the facts, ideas, and considerations and build a data dictionary. This allows me to enter ideas as they come to me without worrying about putting them in any particular order. Once I'm ready, I organize this raw data to develop three types of documentation: a sequential logic flow description, internal program documentation, and public documentation (for example, for the users' manual and promotional material).

Throughout the decomposition process, periodic reviews are important. Going from an overview level to a more detailed one involves a cycle of reviewing your logic at its current level, picking a section and expanding it to greater detail, then reviewing again, and so forth. At each review, run through a checklist of questions: Is the chosen algorithm still valid? Is the logic true to the algorithm? Are the data structures efficient? What operations are common and suitable as subprocedures? What use can I make of procedures presently in my library? Which new procedures should be added to my general library and which are specific to this project? Am I creating a structure that will permit growth?

Action charts are excellent when working with well defined logic, but when your ideas have yet to be solidified, there's nothing wrong with reverting to a less restrictive method. I often make a freehand sketch in an informal flowchart format, and then when the logic is defined, I edit the flowchart sketch directly into an on-line action chart. I'll cover the process of translating flowcharts to action charts in greater detail a little later. As with many parts of this technique, the best way is to experiment and find the methods that suit you best.

A good place to start is to set up the overall layout of the chart by making a bracket that is labeled with the name of your program. (Refer to sidebar "How the Macros Work" details on how to use the macros to make a bracket.) At the top of this first bracket, place a block of comments that describe the purpose of the program and significant details about the implementation. Following this should be a dictionary of global variables and data structure--which may include actual declarative statements in the format of your target language. See Figure 2, page 26.

How the Macros Work

Alt-nnn      Press the decimal value 'nnn' on the numeric keypad while holding down the ALt
key      Alt-c        Press an Alt character combination (e.g. Alt-L)
Shft-F2      Hold down the shift key and press F2
F2           Press F2 alone
up           The up arrow key
down         The down arrow key
left         The left arrow key
right        The right arrow key
space        The space bar
Hrt          The enter key
BS           The backspace key
Home         The Home key
End          The end key

Listed above is the notation that will be used to describe the keystrokes used in making the action chart macros.

 218     | 179      192   - 196

= 205    195    213    212

Shown above are the block graphics characters used to produce action charts and their associated decimal values. When this number is entered on the numeric keypad with the Alt key held down, the corresponding graphic symbol will appear.


The macro Alt-L is used to make a bracket. Before invoking this macro, place the cursor where you want the top left corner of the bracket to be. Two more lines must already exist in the file below your starting point. The keystroke sequence for this macro is:

                 

Alt-218 Alt-196 space left left left down Alt-179 space left left down Alt-192 Alt-196 space
left up up right                             --                       

The macro Alt-P enlarges a bracket by one line. The new line is added below the current one; below the line the cursor is currently in. The keystroke sequence for this macro is:

End Hrt Alt-179

                                                         

The macro Alt-O fills a gap in a bracket with a vertical bar. When enlarg a set of brackets, Alt-P is used to start the new line and insert the vertical bar for the leftmost bracket. Then Alt-O is used to add the vertical bars for all remaining brackets. The keystroke sequence for this macro is:

Alt-179 space

                                 ---                              

The macro Alt-K converts the vertical bar of a bracket to a CD symbol for the ELSE portion of an IF THEN/ELSE statement. Start with the cursor located two columns to the right of the point where you want the CD symbol to appear. The keystroke sequence for this macro is:

                 ---            

The macro Alt-G converts a single line top corner into a double one. Start with the cursor located three columns to the right of the top left corner of the bracket you wish to convert. This is where the cursor will be immediately after using Alt-L to make a bracket. The keystroke sequence for this macro is:

BS BS BS Alt-213 Alt-205 space

       ---               

The macro Alt-B converts a single line bottom corner into a double one. Start with the cursor located three columns to the right of the bottom left corner of the bracket you wish to convert. The keystroke sequence for this macro is:

BS BS BS Alt-212 Alt-205 space

                            --                       

The macro Alt-R copies the line the cursor is on to a new line below. This is especially useful for enlarging a set of nested brackets.

Home Home Home left Alt-F4 down Cntrl-F3 2 Cntrl-F3 5 up End

                         module X         module X      module X        ##
          ##         white(t)        white(t)         white(t)        ....
       ....               ....                                          
                                  cursor location       a                   b
                                                              c

     module X      module X                      if s > 5     
            white(t)         white(t)      ...                ...      
                                                              
          d                  e

Macros Alt-I and Alt-U work together to accomplish the task of inserting a new bracket to the left of one or more levels of pre-existing rackets. In the example above, after the bracket for the while loop was created it was discovered that another test must be added. In order to subordinate the while bracket under a new bracket we must first do a little manual setup. A new line must be inserted just above the bracket to be subordinated (see the Alt-P and Alt-O macros above) and two ## characters must be entered as in (b). Next, a new line must be added just below the bracket to be subordinated. Then, with the cursor located as shown in (c), invoke the Alt-I macro. This will create the top and bottom corners of the new bracket as shown in (d). Now, use the Alt-U to fill in the vertical bars between the new corners.

The keystroke sequence for the Alt-I macro is:

Alt-192 lt-196 space Shft-F2 ## F2 BS BS Alt-218 Alt-196 space left left left down

The keystroke sequence for the Alt-U macro is:

Alt 179 space left left down

By this time, you should have a good idea of what subprocedures you'll be using, although further additions will usually occur as you go through the decomposition process. You may choose to develop the main procedure first, leaving the subprocedures as stubs for now--or you may want to develop the subprocedures first. Either way, make a bracket for each subprocedure within the first bracket and include a statement of purpose and a data header. This header should include what data will be passed in and out, a dictionary of local variables, what global data is referenced and changed, what other procedures are called from this one, and which points in the program call this procedure.

When using precompiled procedures from a library, you may want to keep a file of action-chart excerpts for each procedure. You can then cut and paste these into your working chart file. These excerpts could include just an overview of the procedure or the entire logic flow--as you choose.

Converting a Chart to Source Code

Although action diagrams are useful during the development, testing, and maintenance phases of a project, you cannot feed such documents directly into a compiler. To move from the planning stage into actual code production, you must produce what I'll call a final source code file. This file contains a complete source code version of your program that is ready to be translated into an executable form via an assembler or compiler.

You can use three basic approaches to reach this stage:

    1. Develop the program's logic to a moderate level of detail in action-chart format using pseudocode statements that are detailed enough to be clear but not in the exact syntax of the target language. Then use the action-chart document as a guide while manually typing the actual source code.

    2. Create an action-chart document that contains complete source code statements, and use a simple filter program to remove the brackets. This will either result directly in a final source code file or in a file that requires only a small amount of manual touch-up to reach the final source stage.

    3. Use a more sophisticated filter utility that can interpret the action-chart document and automatically convert it to a final source format, as shown in Figure 3, page 27. This will relieve you from being concerned with an entire layer of detail, such as where to put semicolons and begin/ends or curly braces.

Obviously, the third approach is the best one; however, a filter of that level of complexity is beyond the scope of this article. The filter in Listing One, page 92 (written in Turbo Pascal 3.0), serves as a useful tool when you take the second approach. First I'll examine how this simple filter works and then discuss what could be done to make it more sophisticated.

Figure 2 represents the general logic of a file-processing program. Each line is read in from the input file into a line buffer where it is processed and then written to the output file. In my case, the action-chart document is the input file and the final source code file is the output. The chart in Figure 4, page 32, shows more detail added to both the main body of the program and the subprocedure called advance__index. The string in the variable work__str is parsed from left to right in order to locate the point at which bracket characters end and the actual text begins.

Figure 4: Through the use of a filter program, the graphics characters of an action chart can be removed and text formatting rules applied to control the left margin and identation levels.

          for x := 1 to 100 do          for x:= 1 to 100 do                if x > 20 then
          if x > 20 then                     if odd(x) then                if odd(x) then
          writein('x is odd')           writein('x is odd')                     else
               else                writein('x is even')          writein('x is even')


In the process of passing by the action-chart characters, a nesting-level count is derived that is then used to add the proper indentation to the string before it is written to the destination file. Provisions are made to allow the number of spaces and/or tabs for the left margin to be controlled as well as the number of spaces of indentation per block level. For Pascal, I use no spaces or tabs for the left margin and two spaces per block level. For assembler, I specify one tab for the left margin and zero spaces per block level.

Some enhancements that could be made to the filter program include automatically detecting when a group of statements needs to be surrounded by begin/end keywords (Pascal) or curly braces (C); automatically placing semicolons on the end of statements that need them; and generating labels when working with assembler or a high-level langnage that uses GOT0s, such as Basic. With regard to processing labels, it would be good to allow for user-defined labels (e.g. MAINLOOP, ERROR1, and so on) as well as serialized ones that the filter program would generate when none were explicitly specified (for example, A0001, A0002, and so on). Labels can be shared by more than one action-chart construct when they coincide because of nesting or by being adjacent, and they can be placed in column 1 of the output file even when tabs or spaces are specified for the left margin.

When translating from action-chart logic to assembler, note the inverse relationship between the high-level if statement of an action-chart and the resulting assembler code, which will test and jump based on the opposite condition. Figure 5, page 32, illustrates an expression of the type If ax > data1, which is a positive logic statement, and the resulting assembler code, which is of the negative logic form. In Figure 5a, what actually needs to be stated is, "If ax is not greater than datai then skip the next two instructions." To keep the association between a program's action chart and its source code direct, you can use a macro like that shown in Figure 5. The assembler code in Figure 5c would then read "Flow through if ax is greater than data1, else skip to the label x1." The fourth macro parameter, else, is added merely for readability.

Figure 5: A statement of the type "if ax > data1" is a positive logic expression; the resulting assembler code would be of the negative logic form. In (a), it needs to be stated that if ax is NOT greater than data1, then the next two instructions must be skipped. In (b), a macro is used to keep the association between the program's action chart and its source code more direct. In (c), the assembler code would read "flow through if ax is above data1, else skip to the label x1."


(a)

                   if ax > data1                   si = 0                   di = 0
       

(b)

flowif MACRO p1, p2, p3, p4, p5 cmp p1, p3 ifidn <&p2>, <ne> je p5 else jn&p2 p5 endif ENDM

(c)

                    flowif    ax a data1 else x1                     xor       si, si
           xor       di, di

          x1:

         

Translating Flowcharts to Action Charts and Reverse Charting

When you work with code you've never seen before, or even a complex piece of code you wrote some time ago, reverse engineering it into an action chart can be a great aid to understanding it in depth. When working with a high-level language and well-structured code, going from source code to an action chart is relatively easy. When a clear structure does not exist, however, or when it is not immediately apparent (as can often be the case with assembler), an intermediate representation in flowchart format may be necessary. The fact that flowcharts do not impose any particular structure can actually be of benefit.

In working to reverse chart existing code, it is not uncommon to find logic that cannot be translated directly into an action chart. In such cases I often sketch a flowchart of the code, including only enough detail to match the chart to the source code (for example, just significant line numbers or labels and minimal comments). Next, I translate this into an action-chart skeleton, carrying over the minimal comments as a link with the original. Using this link, I can then look back at the original source code and fill in the details. In many cases you will need to transform the logic to achieve a structure that will fit properly within the rules of action diagrams. When you find sections of code being entered or exited via multiple program paths, you can often isolate the shared code and convert it into a subprocedure that can then be called from each path in a structured manner.

Conclusions

Although using macros with your present editor lets you begin utilizing this tool immediately, an editor designed especially for action-chart development could offer many powerful features and conveniences. Applying these methods in an editor with a more sophisticated macro facility should also offer many interesting possibilities. In addition, more sophisticated filters and translation programs that produce action-chart documents from existing source code will make the work of programming much easier.

I see a growing movement toward a widespread application of software engineering tools. Most of the tools now available, however, focus on the design and verification of data structures to the exclusion of procedural logic development. Action charts and the implementation of common-keystroke macros within an editor are a way of making a certain class of software engineering tools immediately available to anyone. By encouraging the use of such tools, not only will the quality of software development improve but the range of available tools will increase as well.

_USING ACTION CHARTS_ by Martin Stitt

[LISTING ONE]

<a name="0196_000c">

{ Read each line of an action chart file and filter out the bracket }
{ charactors.  Format the remaining text with regards to the left }
{ margin and indentation. }
{ NOTE - comments ending with a ** denote areas of this program which were }
{        left incomplete for the sake of brevity. }
{ You should customize the following constants to suit your particular }
{ tastes as far as indentation.  You may wish to use variables instead }
{ and obtain their values from command line switches or a configuration }
{ file. }

const

  margin_spaces    : integer = 0;         { customize these variables per }
  margin_tabs      : integer = 0;         { your particular taste. }
  spaces_per_level : integer = 2;         { Better yet, prompt for them at }
  skip_null_lines  : boolean = false;     { run time or read from a

{ These character sets are the block graphics characters which }
{ this program filters out. }

  top_set  : set of char = ['Z','U'];    { alt-218, alt-213 }
  bot_set  : set of char = ['@','T'];    { alt-192, alt-212 }
  else_set : set of char = ['C'];        { alt-195 }
  pass_set : set of char = [' ','D','M','3'];

                                         { space, alt-196, alt-205, alt-179 }
type
  str_type = string[80];
  chset = set of char;

var
  source,dest       : text;
  total_spaces,c_index,
  indent_adj,indent_level : integer;
  indent_str,work_str : str_type;


procedure advance_index(w_str : str_type; var ix : integer; test_set : chset);

{ accept a string, an index and a set of characters to the string test for. }
{ advance the index past all charactors in the specified set, returning an }
{ updated value of the index to the calling program. }

var
  w_str_len : integer;

begin
w_str_len := ord(w_str[0]);
while(true) do begin
  if ix > w_str_len then exit;
  if not (w_str[ix] in test_set) then exit;
  ix := ix + 1
  end
end;

begin  { MAIN PROGRAM LOGIC}

indent_level := 0;

{ validate command line parameters and manage file open errors ** }

assign(source,paramstr(1));
reset(source);

assign(dest,paramstr(2));
rewrite(dest);

{ read each line from the source file and filter out the characters which }
{ are used to produce the action chart brackets.  Write the resulting line }
{ to the destination file. }

while(not eof(source)) do begin
  readln(source,work_str);
  c_index := 1;
  indent_adj := 0;
  advance_index(work_str,c_index,pass_set);
  if c_index <= ord(work_str[0]) then
    if work_str[c_index] in top_set + bot_set + else_set then begin
      if work_str[c_index] in top_set then indent_adj := 1
      else
        if work_str[c_index] in bot_set then
          indent_level := indent_level - 1
        else begin     { must be in the else set }
          indent_level := indent_level - 1;
          indent_adj := 1
          end;
      advance_index(work_str,c_index,top_set + bot_set + else_set + pass_set);
      end;
  if c_index <= ord(work_str[0]) then begin
    total_spaces := margin_spaces + (indent_level * spaces_per_level);
    indent_str[0] := chr(margin_tabs + total_spaces);
    fillchar(indent_str[1],margin_tabs,#9);
    fillchar(indent_str[margin_tabs+1],total_spaces,' ');
    writeln(dest,concat(indent_str,copy(work_str,c_index,999)))
    end
  else
    if not skip_null_lines then writeln(dest);
  indent_level := indent_level + indent_adj
  end;

close(dest);
close(source);

{ manage file errors ** }

end.












Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.