source: doc/theses/jiada_liang_MMath/CFAenum.tex @ 878b1385

Last change on this file since 878b1385 was 6f47834, checked in by Peter A. Buhr <pabuhr@…>, 4 months ago

add section on enumeration I/O

  • Property mode set to 100644
File size: 22.0 KB
Line 
1\chapter{\CFA Enumeration}
2
3
4\CFA supports C enumeration using the same syntax and semantics for backwards compatibility.
5\CFA also extends C-Style enumeration by adding a number of new features that bring enumerations inline with other modern programming languages.
6Any enumeration extensions must be intuitive to C programmers both in syntax and semantics.
7The following sections detail all of my new contributions to enumerations in \CFA.
8
9
10\section{Aliasing}
11
12{\color{red}@***@}
13C already provides @const@-style aliasing using the unnamed enumerator \see{\VRef{s:TypeName}}, even if the keyword @enum@ is misleading (@const@ is better).
14However, given the existence of this form, it is straightforward to extend it with heterogeneous types, \ie types other than @int@.
15\begin{cfa}
16enum { Size = 20u, PI = 3.14159L, Jack = L"John" }; $\C{// not an ADT nor an enumeration}$
17\end{cfa}
18which matches with @const@ aliasing in other programming languages.
19(See \VRef{s:CenumImplementation} on how @gcc@/@clang@ are doing this for integral types.)
20Here, the type of each enumerator is the type of the initialization constant, \eg @typeof(20u)@ for @Size@ implies @unsigned int@.
21Auto-initialization is impossible in this case because some types do not support arithmetic.
22As seen in \VRef{s:EnumeratorTyping}, this feature is just a shorthand for multiple typed-enumeration declarations.
23
24
25\section{Enumerator Visibility}
26\label{s:EnumeratorVisibility}
27
28In C, unscoped enumerators present a \newterm{naming problem} when multiple enumeration types appear in the same scope with duplicate enumerator names.
29There is no mechanism in C to resolve these naming conflicts other than renaming one of the duplicates, which may be impossible if the conflict comes from system include files.
30
31The \CFA type-system allows extensive overloading, including enumerators.
32Furthermore, \CFA uses the environment, such as the left-hand of assignment and function arguments, to pinpoint the best overloaded name.
33\VRef[Figure]{f:EnumeratorVisibility} shows enumeration overloading and how qualification and casting are used to disambiguate ambiguous situations.
34\CFA overloading allows programmers to use the most meaningful names without fear of name clashes within a program or from external sources, like include files.
35Experience from \CFA developers is that the type system implicitly and correctly disambiguates the majority of overloaded names.
36That is, it is rare to get an incorrect selection or ambiguity, even among hundreds of overloaded variables and functions, that requires disambiguation using qualification or casting.
37
38\begin{figure}
39\begin{cfa}
40enum E1 { First, Second, Third, Fourth };
41enum E2 { @Fourth@, @Third@, @Second@, @First@ }; $\C{// same enumerator names}$
42E1 f() { return Third; }                                $\C{// overloaded functions, different return types}$
43E2 f() { return Fourth; }
44void g( E1 e );
45void h( E2 e );
46void foo() {                                                    $\C{// different resolutions and dealing with ambiguities}$
47        E1 e1 = First;   E2 e2 = First;         $\C{// initialization}$
48        e1 = Second;   e2 = Second;                     $\C{// assignment}$
49        e1 = f();   e2 = f();                           $\C{// function return}$
50        g( First );   h( First );                       $\C{// function argument}$
51        int i = @E1.@First + @E2.@First;        $\C{// disambiguate with qualification}$
52        int j = @(E1)@First + @(E2)@First;      $\C{// disambiguate with cast}$
53}
54\end{cfa}
55\caption{Enumerator Visibility and Disambiguating}
56\label{f:EnumeratorVisibility}
57\end{figure}
58
59
60\section{Enumerator Scoping}
61
62An enumeration can be scoped, using @'!'@, so the enumerator constants are not projected into the enclosing scope.
63\begin{cfa}
64enum Week @!@ { Mon, Tue, Wed, Thu = 10, Fri, Sat, Sun };
65enum RGB @!@ { Red, Green, Blue };
66\end{cfa}
67Now the enumerators \emph{must} be qualified with the associated enumeration type.
68\begin{cfa}
69Week week = @Week.@Mon;
70week = @Week.@Sat;
71RGB rgb = @RGB.@Red;
72rgb = @RGB.@Blue;
73\end{cfa}
74{\color{red}@***@}It is possible to toggle back to unscoped using the \CFA @with@ clause/statement (see also \CC \lstinline[language=c++]{using enum} in Section~\ref{s:C++RelatedWork}).
75\begin{cfa}
76with ( @Week@, @RGB@ ) {                                $\C{// type names}$
77         week = @Sun@;                                          $\C{// no qualification}$
78         rgb = @Green@;
79}
80\end{cfa}
81As in Section~\ref{s:EnumeratorVisibility}, opening multiple scoped enumerations in a @with@ can result in duplicate enumeration names, but \CFA implicit type resolution and explicit qualification/casting handle this localized scenario.
82
83
84\section{Enumerator Typing}
85\label{s:EnumeratorTyping}
86
87\CFA extends the enumeration declaration by parameterizing with a type (like a generic type), allowing enumerators to be assigned any values from the declared type.
88Figure~\ref{f:EumeratorTyping} shows a series of examples illustrating that all \CFA types can be use with an enumeration and each type's constants used to set the enumerator constants.
89Note, the synonyms @Liz@ and @Beth@ in the last declaration.
90Because enumerators are constants, the enumeration type is implicitly @const@, so all the enumerator types in Figure~\ref{f:EumeratorTyping} are logically rewritten with @const@.
91
92C has an implicit type conversion from an enumerator to its base type @int@.
93Correspondingly, \CFA has an implicit (safe) conversion from a typed enumerator to its base type.
94\begin{cfa}
95char currency = Dollar;
96string fred = Fred;                                             $\C{// implicit conversion from char * to \CFA string type}$
97Person student = Beth;
98\end{cfa}
99
100% \begin{cfa}
101% struct S { int i, j; };
102% enum( S ) s { A = { 3,  4 }, B = { 7,  8 } };
103% enum( @char@ ) Currency { Dollar = '$\textdollar$', Euro = '$\texteuro$', Pound = '$\textsterling$'  };
104% enum( @double@ ) Planet { Venus = 4.87, Earth = 5.97, Mars = 0.642  }; // mass
105% enum( @char *@ ) Colour { Red = "red", Green = "green", Blue = "blue"  };
106% enum( @Currency@ ) Europe { Euro = '$\texteuro$', Pound = '$\textsterling$' }; // intersection
107% \end{cfa}
108
109\begin{figure}
110\begin{cfa}
111// integral
112        enum( @char@ ) Currency { Dollar = '$\textdollar$', Cent = '$\textcent$', Yen = '$\textyen$', Pound = '$\textsterling$', Euro = 'E' };
113        enum( @signed char@ ) srgb { Red = -1, Green = 0, Blue = 1 };
114        enum( @long long int@ ) BigNum { X = 123_456_789_012_345,  Y = 345_012_789_456_123 };
115// non-integral
116        enum( @double@ ) Math { PI_2 = 1.570796, PI = 3.141597, E = 2.718282 };
117        enum( @_Complex@ ) Plane { X = 1.5+3.4i, Y = 7+3i, Z = 0+0.5i };
118// pointer
119        enum( @const char *@ ) Name { Fred = "FRED", Mary = "MARY", Jane = "JANE" };
120        int i, j, k;
121        enum( @int *@ ) ptr { I = &i,  J = &j,  K = &k };
122@***@enum( @int &@ ) ref { I = i,   J = j,   K = k };
123// tuple
124@***@enum( @[int, int]@ ) { T = [ 1, 2 ] }; $\C{// new \CFA type}$
125// function
126        void f() {...}   void g() {...}
127        enum( @void (*)()@ ) funs { F = f,  G = g };
128// aggregate
129        struct Person { char * name; int age, height; };
130@***@enum( @Person@ ) friends { @Liz@ = { "ELIZABETH", 22, 170 }, @Beth@ = Liz,
131                                                                        Jon = { "JONATHAN", 35, 190 } };
132\end{cfa}
133\caption{Enumerator Typing}
134\label{f:EumeratorTyping}
135\end{figure}
136
137An advantage of the typed enumerations is eliminating the \emph{harmonizing} problem between an enumeration and companion data \see{\VRef{s:Usage}}:
138\begin{cfa}
139enum( char * ) integral_types {
140        chr = "char", schar = "signed char", uschar = "unsigned char",
141        sshort = "signed short int", ushort = "unsigned short int",
142        sint = "signed int", usint = "unsigned int",
143        ...
144};
145\end{cfa}
146Note, the enumeration type can be a structure (see @Person@ in Figure~\ref{f:EumeratorTyping}), so it is possible to have the equivalent of multiple arrays of companion data using an array of structures.
147
148While the enumeration type can be any C aggregate, the aggregate's \CFA constructors are not used to evaluate an enumerator's value.
149\CFA enumeration constants are compile-time values (static);
150calling constructors happens at runtime (dynamic).
151
152
153\section{Opaque Enumeration}
154
155\CFA provides a special opaque (pure) enumeration type with only assignment and equality operations, and no implicit conversion to any base-type.
156\begin{cfa}
157enum@()@ Mode { O_RDONLY, O_WRONLY, O_CREAT, O_TRUNC, O_APPEND };
158Mode mode = O_RDONLY;
159if ( mode == O_CREAT ) ...
160bool b = mode == O_RDONLY || mode @<@ O_APPEND; $\C{// disallowed}$
161int www @=@ mode;                                               $\C{// disallowed}$
162\end{cfa}
163
164
165\section{Enumeration Operators}
166
167
168\subsection{Conversion}
169
170\CFA only proves an implicit safe conversion between an enumeration and its base type (like \CC), whereas C allows an unsafe conversion from base type to enumeration.
171\begin{cfa}
172enum(int) Colour { Red, Blue, Green };
173int w = Red;            $\C[1.5in]{// allowed}$
174Colour color = 0;       $\C{// disallowed}\CRT$
175\end{cfa}
176Unfortunately, there must be one confusing case between C enumerations and \CFA enumeration for type @int@.
177\begin{cfa}
178enum Colour { Red = 42, Blue, Green };
179enum(int) Colour2 { Red = 16, Blue, Green };
180int w = Redy;           $\C[1.5in]{// 42}\CRT$
181\end{cfa}
182Programmer intuition is that the assignment to @w@ is ambiguous.
183However, converting from @color@ to @int@ is zero cost (no conversion), while from @Colour2@ to @int@ is a safe conversion, which is a higher cost.
184This semantics means fewer backwards-compatibility issues with overloaded C and \CFA enumerators.
185
186
187\subsection{Properties}
188
189\VRef{s:Terminology} introduced three fundamental enumeration properties: label, position, and value.
190\CFA provides direct access to these three properties via the functions: @label@, @posn@, and @value@.
191\begin{cfa}
192enum( const char * ) Name { Fred = "FRED", Mary = "MARY", Jane = "JANE" };
193Name name = Fred;
194sout | name | label( name ) | posn( name ) | value( name );
195FRED Fred 0 FRED
196\end{cfa}
197The default meaning for an enumeration variable in an expression is its value.
198
199
200\subsection{Range}
201
202The following helper function are used to access and control enumeration ranges (enumerating).
203
204The pseudo-function @countof@ (like @sizeof@) provides the size (range) of an enumeration or an enumeration instance.
205\begin{cfa}
206enum(int) Colour { Red, Blue, Green };
207Colour c = Red
208sout | countof( Colour ) | countof( c );
2093 3
210\end{cfa}
211@countof@ is a pseudo-function because it takes a type as an argument.
212The function @fromInt@ provides a safe subscript of the enumeration.
213\begin{cfa}
214Colour r = fromInt( prng( countof( Colour ) ) ); // select random colour
215\end{cfa}
216The functions @lowerBound@, @upperBound@, @succ@, and @pred@ are for enumerating.
217\begin{cfa}
218for ( Colour c = lowerBound();; ) {
219        sout | c | nonl;
220  if ( c == upperBound() ) break;
221        c = succ( c );
222}
223\end{cfa}
224Note, the mid-exit loop is necessary to prevent triggering a @succ@ bound check, as in:
225\begin{cfa}
226for ( Colour c = lowerBound(); c <= upperBound(); c = succ( c ) ) ... // generates error
227\end{cfa}
228When @c == upperBound()@, the loop control still invokes @succ( c )@, which causes an @enumBound@ exception.
229Finally, there is operational overlap between @countof@ and @upperBound@.
230
231
232\section{Enumeration Inheritance}
233
234\CFA Plan-9 inheritance may be used with enumerations, where Plan-9 inheritance is containment inheritance with implicit unscoping (like a nested unnamed @struct@/@union@ in C).
235\begin{cfa}
236enum( const char * ) Names { Fred = "FRED", Mary = "MARY", Jane = "JANE" };
237enum( const char * ) Names2 { @inline Names@, Jack = "JACK", Jill = "JILL" };
238enum( const char * ) Names3 { @inline Names2@, Sue = "SUE", Tom = "TOM" };
239\end{cfa}
240Enumeration @Name2@ inherits all the enumerators and their values from enumeration @Names@ by containment, and a @Names@ enumeration is a subtype of enumeration @Name2@.
241Note, that enumerators must be unique in inheritance but enumerator values may be repeated.
242
243% The enumeration type for the inheriting type must be the same as the inherited type;
244% hence the enumeration type may be omitted for the inheriting enumeration and it is inferred from the inherited enumeration, as for @Name3@.
245% When inheriting from integral types, automatic numbering may be used, so the inheritance placement left to right is important.
246Specifically, the inheritance relationship for @Names@ is:
247\begin{cfa}
248Names  $\(\subset\)$  Names2  $\(\subset\)$  Names3  $\C{// enum type of Names}$
249\end{cfa}
250A subtype can be cast to its supertype, assigned to a supertype variable, or used as a function argument that expects the supertype.
251\begin{cfa}
252Names fred = Names.Fred;
253(Names2)fred;   (Names3)fred;   (Names3)Names2.Jack;  $\C{// cast to super type}$
254Names2 fred2 = fred;   Names3 fred3 = fred2;    $\C{// assign to super type}$
255\end{cfa}
256As well, there is the implicit cast to an enumerator's base-type.
257\begin{cfa}
258const char * name = fred;
259\end{cfa}
260For the given function prototypes, the following calls are valid.
261\begin{cquote}
262\begin{tabular}{ll}
263\begin{cfa}
264void f( Names );
265void g( Names2 );
266void h( Names3 );
267void j( const char * );
268\end{cfa}
269&
270\begin{cfa}
271f( Fred );
272g( Fred );   g( Jill );
273h( Fred );   h( Jill );   h( Sue );
274j( Fred );    j( Jill );    j( Sue );    j( "WILL" );
275\end{cfa}
276\end{tabular}
277\end{cquote}
278Note, the validity of calls is the same for call-by-reference as for call-by-value, and @const@ restrictions are the same as for other types.
279
280
281\section{Enumerator Control Structures}
282
283Enumerators can be used in multiple contexts.
284In most programming languages, an enumerator is implicitly converted to its value (like a typed macro substitution).
285However, enumerator synonyms and typed enumerations make this implicit conversion to value incorrect in some contexts.
286In these contexts, a programmer's initition assumes an implicit conversion to position.
287
288For example, an intuitive use of enumerations is with the \CFA @switch@/@choose@ statement, where @choose@ performs an implicit @break@ rather than a fall-through at the end of a @case@ clause.
289(For this discussion, ignore the fact that @case@ requires a compile-time constant.)
290\begin{cfa}[belowskip=0pt]
291enum Count { First, Second, Third, Fourth };
292Count e;
293\end{cfa}
294\begin{cquote}
295\setlength{\tabcolsep}{15pt}
296\noindent
297\begin{tabular}{@{}ll@{}}
298\begin{cfa}[aboveskip=0pt]
299
300choose( e ) {
301        case @First@: ...;
302        case @Second@: ...;
303        case @Third@: ...;
304        case @Fourth@: ...;
305}
306\end{cfa}
307&
308\begin{cfa}[aboveskip=0pt]
309// rewrite
310choose( @value@( e ) ) {
311        case @value@( First ): ...;
312        case @value@( Second ): ...;
313        case @value@( Third ): ...;
314        case @value@( Fourth ): ...;
315}
316\end{cfa}
317\end{tabular}
318\end{cquote}
319Here, the intuitive code on the left is implicitly transformed into the standard implementation on the right, using the value of the enumeration variable and enumerators.
320However, this implementation is fragile, \eg if the enumeration is changed to:
321\begin{cfa}
322enum Count { First, Second, Third @= First@, Fourth };
323\end{cfa}
324making @Third == First@ and @Fourth == Second@, causing a compilation error because of duplicate @case@ clauses.
325To better match with programmer intuition, \CFA toggles between value and position semantics depending on the language context.
326For conditional clauses and switch statements, \CFA uses the robust position implementation.
327\begin{cfa}
328if ( @posn@( e ) < posn( Third ) ) ...
329choose( @posn@( e ) ) {
330        case @posn@( First ): ...;
331        case @posn@( Second ): ...;
332        case @posn@( Third ): ...;
333        case @posn@( Fourth ): ...;
334}
335\end{cfa}
336
337\CFA provides a special form of for-control for enumerating through an enumeration, where the range is a type.
338\begin{cfa}
339for ( cx; @Count@ ) { sout | cx | nonl; } sout | nl;
340for ( cx; +~= Count ) { sout | cx | nonl; } sout | nl;
341for ( cx; -~= Count ) { sout | cx | nonl; } sout | nl;
342First Second Third Fourth
343First Second Third Fourth
344Fourth Third Second First
345\end{cfa}
346The enumeration type is syntax sugar for looping over all enumerators and assigning each enumerator to the loop index, whose type is inferred from the range type.
347The prefix @+~=@ or @-~=@ iterate forward or backwards through the inclusive enumeration range, where no prefix defaults to @+~=@.
348
349C has an idiom for @if@ and loop predicates of comparing the predicate result ``not equal to 0''.
350\begin{cfa}
351if ( x + y /* != 0 */ ) ...
352while ( p /* != 0 */ ) ...
353\end{cfa}
354This idiom extends to enumerations because there is a boolean conversion in terms of the enumeration value, if and only if such a conversion is available.
355For example, such a conversion exists for all numerical types (integral and floating-point).
356It is possible to explicitly extend this idiom to any typed enumeration by overloading the @!=@ operator.
357\begin{cfa}
358bool ?!=?( Name n, zero_t ) { return n != Fred; }
359Name n = Mary;
360if ( n ) ... // result is true
361\end{cfa}
362Specialize meanings are also possible.
363\begin{cfa}
364enum(int) ErrorCode { Normal = 0, Slow = 1, Overheat = 1000, OutOfResource = 1001 };
365bool ?!=?( ErrorCode ec, zero_t ) { return ec >= Overheat; }
366ErrorCode code = ...;
367if ( code ) { problem(); }
368\end{cfa}
369
370
371\section{Enumeration Dimension}
372
373\VRef{s:EnumeratorTyping} introduced the harmonizing problem between an enumeration and secondary information.
374When possible, using a typed enumeration for the secondary information is the best approach.
375However, there are times when combining these two types is not possible.
376For example, the secondary information might precede the enumeration and/or its type is needed directly to declare parameters of functions.
377In these cases, having secondary arrays of the enumeration size are necessary.
378
379To support some level of harmonizing in these cases, an array dimension can be defined using an enumerator type, and the enumerators used as subscripts.
380\begin{cfa}
381enum E { A, B, C, N }; // possibly predefined
382float H1[N] = { [A] : 3.4, [B] : 7.1, [C] : 0.01 }; // C
383float H2[@E@] = { [A] : 3.4, [B] : 7.1, [C] : 0.01 }; // CFA
384\end{cfa}
385(Note, C uses the symbol, @'='@ for designator initialization, but \CFA had to change to @':'@ because of problems with tuple syntax.)
386This approach is also necessary for a predefined typed enumeration (unchangeable), when additional secondary-information need to be added.
387
388
389\section{Enumeration I/O}
390
391As seen in multiple examples, enumerations can be printed and the default property printed is the enumerator's label, which is similar in other programming languages.
392However, very few programming languages provide a mechanism to read in enumerator values.
393Even the @boolean@ type in many languages does not have a mechanism for input using the enumerators @true@ or @false@.
394\VRef[Figure]{f:EnumerationI/O} show \CFA enumeration input based on the enumerator labels.
395When the enumerator labels are packed together in the input stream, the input algorithm scans for the longest matching string.
396For basic types in \CFA, the constants use to initialize a variable in a program are available to initialize a variable using input, where strings constants can be quoted or unquoted.
397
398\begin{figure}
399\begin{cquote}
400\setlength{\tabcolsep}{15pt}
401\begin{tabular}{@{}ll@{}}
402\begin{cfa}
403int main() {
404        enum(int ) E { BBB = 3, AAA, AA, AB, B };
405        E e;
406
407        for () {
408                try {
409                        @sin | e@;
410                } catch( missing_data * ) {
411                        sout | "missing data";
412                        continue; // try again
413                }
414          if ( eof( sin ) ) break;
415                sout | e | "= " | value( e );
416        }
417}
418\end{cfa}
419&
420\begin{cfa}
421$\rm input$
422BBBABAAAAB
423BBB AAA AA AB B
424
425$\rm output$
426BBB = 3
427AB = 6
428AAA = 4
429AB = 6
430BBB = 3
431AAA = 4
432AA = 5
433AB = 6
434B = 7
435
436\end{cfa}
437\end{tabular}
438\end{cquote}
439\caption{Enumeration I/O}
440\label{f:EnumerationI/O}
441\end{figure}
442
443
444
445\section{Planet Example}
446
447\VRef[Figure]{f:PlanetExample} shows an archetypal enumeration example illustrating most of the \CFA enumeration features.
448@Planet@ is an enumeration of type @MR@.
449Each planet enumerator is initialized to a specific mass/radius, @MR@, value.
450The unnamed enumeration provides the gravitational-constant enumerator @G@.
451Function @surfaceGravity@ uses the @with@ clause to remove @p@ qualification from fields @mass@ and @radius@.
452The program main uses the pseudo function @countof@ to obtain the number of enumerators in @Planet@, and safely converts the random value into a @Planet@ enumerator using @fromInt@.
453The resulting random orbital-body is used in a @choose@ statement.
454The enumerators in the @case@ clause use the enumerator position for testing.
455The prints use @label@ to print an enumerator's name.
456Finally, a loop enumerates through the planets computing the weight on each planet for a given earth mass.
457The print statement does an equality comparison with an enumeration variable and enumerator (@p == MOON@).
458
459\begin{figure}
460\small
461\begin{cfa}
462struct MR { double mass, radius; };                     $\C{// planet definition}$
463enum( @MR@ ) Planet {                                           $\C{// typed enumeration}$
464        //                      mass (kg)   radius (km)
465        MERCURY = { 0.330_E24, 2.4397_E6 },
466        VENUS      = { 4.869_E24, 6.0518_E6 },
467        EARTH       = { 5.976_E24, 6.3781_E6 },
468        MOON        = { 7.346_E22, 1.7380_E6 }, $\C{// not a planet}$
469        MARS         = { 0.642_E24, 3.3972_E6 },
470        JUPITER    = { 1898._E24, 71.492_E6 },
471        SATURN     = { 568.8_E24, 60.268_E6 },
472        URANUS    = { 86.86_E24, 25.559_E6 },
473        NEPTUNE  = { 102.4_E24, 24.746_E6 },
474        PLUTO       = { 1.303_E22, 1.1880_E6 }, $\C{// not a planet}$
475};
476enum( double ) { G = 6.6743_E-11 };                     $\C{// universal gravitational constant (m3 kg-1 s-2)}$
477static double surfaceGravity( Planet p ) @with( p )@ {
478        return G * mass / ( radius @\@ 2 );             $\C{// no qualification, exponentiation}$
479}
480static double surfaceWeight( Planet p, double otherMass ) {
481        return otherMass * surfaceGravity( p );
482}
483int main( int argc, char * argv[] ) {
484        if ( argc != 2 ) @exit@ | "Usage: " | argv[0] | "earth-weight";  // terminate program
485        double earthWeight = convert( argv[1] );
486        double earthMass = earthWeight / surfaceGravity( EARTH );
487        Planet rp = @fromInt@( prng( @countof@( Planet ) ) ); $\C{// select random orbiting body}$
488        @choose( rp )@ {                                                $\C{// implicit breaks}$
489          case MERCURY, VENUS, EARTH, MARS:
490                sout | @rp@ | "is a rocky planet";
491          case JUPITER, SATURN, URANUS, NEPTUNE:
492                sout | rp | "is a gas-giant planet";
493          default:
494                sout | rp | "is not a planet";
495        }
496        for ( @p; Planet@ ) {                                   $\C{// enumerate}$
497                sout | "Your weight on" | ( @p == MOON@ ? "the" : " " ) | p
498                           | "is" | wd( 1,1,  surfaceWeight( p, earthMass ) ) | "kg";
499        }
500}
501$\$$ planet 100
502JUPITER is a gas-giant planet
503Your weight on MERCURY is 37.7 kg
504Your weight on VENUS is 90.5 kg
505Your weight on EARTH is 100.0 kg
506Your weight on the MOON is 16.6 kg
507Your weight on MARS is 37.9 kg
508Your weight on JUPITER is 252.8 kg
509Your weight on SATURN is 106.6 kg
510Your weight on URANUS is 90.5 kg
511Your weight on NEPTUNE is 113.8 kg
512Your weight on PLUTO is 6.3 kg
513\end{cfa}
514\caption{Planet Example}
515\label{f:PlanetExample}
516\end{figure}
Note: See TracBrowser for help on using the repository browser.