[dir] Zum Verzeichnisinhalt     [exercise] Zur Musterlösung     [download] (SHIFT + Linke Maustaste)
@=~
~p maximum_input_line_length = infinity

~A~<Switch Case Labels~>

Switch statements sometimes span over a wide range of case label
values, and only a few case labels are stated for the non-default cases.
A poor C compiler might then produce a large jump table with only a few
non-default entries. Hence the style guide requires not to waste
storage by sparse switch cases. 

You decide to suggest to your manager
that this requirement should be removed, because today's C compiler
automatically translate sparse switch cases into cascades of conditional
jumps. 

In order to strengthen your argument by figures, your analysis
tool will compute the number of the case labels in a switch statement
and the range of their values.

For a program like

~O~<switch.in~>~{~-
{ int i, z;
  i = read();
  switch (i)
  {
   case 0:   z = 1;
             break;
   case 99:
   case 100: z = 2;
             break;
   default:  z = 0;
  }
  print (z);
}
~}

the tool will produce the output:

~O~<switch.out~>~{~-
switch in line 3 has 3 case labels in the range 0 to 100
~}

The following questions may help to focus your attention on the
significant aspects of this problem:

1. How do you access the case label values from the switch statement
context?

2. Which functions do you need to combine the values?

3. How do you handle nested switch statements?


~B~<Solution~>

For each ~{SwStmt~} the number of its ~{CaseLit~} occurrences and
their smalles and largest value are computed by three CONSTITUENTS
constructs, each combining the ~{CaseLit~} values by an appropriate
triple of functions: 
	ADD, ARGTOONE, ZERO or 
	MIN, IDENTICAL, f_INT_MAX or 
	MAX, IDENTICAL, f_INT_MIN. 

The functions ~{f2, f1, f0~} are chosen
such that ~{f1~} yields the appropriate value of a single item,
and for any ~{x~} the equation ~{f2 (x, f0()) = x~} holds.

The first CONSTITUENTS doesn't use the values of ~{CaseLit.Sym~} at all
for counting, since ~{ARGTOONE~} is a constant (unary!) function, that
yields 1 for any argument. 
~{f_INT_MAX~} and ~{f_INT_MIN~} are constant
functions that yield the largest repectively the smallest int value.

In case of nested switch statements the CONSTITUENTS do not access
~{CaseLit~} occurrences of inner switch statements. This fact could
be made explicit by using

~{CONSTITUENTS CaseLit.Sym SHIELD (SwStmt) WITH ...~}

The SYMBOL computation for ~{CaseLit~} acesses the value of the 
terminal ~{Number~}.

~$~<Switch case label computation~>~{
ATTR Sym: int;

SYMBOL SwStmt COMPUTE
  printf ("switch in line %d has %d case labels in the range %d to %d\n",
	LINE,
	CONSTITUENTS CaseLit.Sym WITH (int, ADD, ARGTOONE, ZERO),
	CONSTITUENTS CaseLit.Sym WITH (int, MIN, IDENTICAL, f_INT_MAX),
	CONSTITUENTS CaseLit.Sym WITH (int, MAX, IDENTICAL, f_INT_MIN));
END;

SYMBOL CaseLit COMPUTE SYNT.Sym = TERM; END;
~}

~{ADD~}, ~{ZERO~}, ~{ARGTOONE~}, and ~{IDENTICAL~} 
are predefined macros in LIDO. 
The other functions are supplied by the following cpp macros:

~$~<Cpp macro definitions~>~{
#include <limits.h>
#define MIN(x,y)	((x)<=(y) ? (x) : (y))
#define MAX(x,y)	((x)>=(y) ? (x) : (y))
#define f_INT_MIN()	INT_MIN
#define f_INT_MAX()	INT_MAX
~}

~B~<Further Questions~>

1. Does your solution work for nested switch statements?
Explain why.

2. Explain why the ~{f0~} functions are needed
(look at the grammar).

3. Can you make any assumption about the order in which
the functions are called?

4. Reconsider your solution when you have learned about
SYMBOL computations. How can you use them to simplify
your solution?

~B~<Output files~>

~O~<switch.lido~>~{
~<Switch case label computation~>
~}

~O~<switch.head~>~{
~<Cpp macro definitions~>
~}