Two lab sessions (Feb 2nd and 9th) have been allocated for working through lab 2 and beginning your work on assignment 2, which is due on February 13th.
Lab 2 part A: intro to the C preprocessor/macros
The first half of lab 2 will focus on working with the core features of the C preprocessor and macros, with an emphasis on the basic transformation aspects applicable to assignment 2 (implementing dimento features through C macros).
An overview of the basic syntax and use of C preprocessor directives is given here, and some macro examples here.
In terms of the dimento features, most can be created with relatively simple #defines, some relevant suggestions are included below.
One suitable printf statement to get the desired behaviour would be
Straight substition can fill in most of the pattern, the only catch
is substituting a text representation of the expression itself.
Fortunately that is exactly what the # operator does for us.
printf("%s = %g\n", EXPRTEXT, (expr) );
the C ?: operator will be useful for each of these.
In conventional boolean logic (x?y:0) returns 0 if either variable is 0,
y otherwise, giving behaviour close to a logical and, (x?0:1) for not,
(x?1:y) for or.
Since we have ternary logic to represent, you'll need to use nested
:? statements, and possibly use test(x) or test(y) to ensure the
returned values are always one of -1/0/1
Lab 2 part B: implementing dimento function declarations
The second half of lab 2 will focus on iterative and recursive C macros - understanding how they work and how they can be applied to the function declaration feature (func) of dimento.
The core of the problem with function declarations is that we want to transform
something like func(fname,x,y,z) into something like
float fname(float x, float y, float z)
The "float fname(" part at the beginning, and the ")" at the end should be easy by the time you've finished part A above, the real trick is getting the "float " before the first parameter and ", float " before each of the remaining parameters.
If we try to replace the pattern func(fname,...), we can use __VA_ARGS__ to get the list of variables, but somehow we need to then rewrite the __VA_ARGS__ content, inserting the floats and commas in the right spots.
As mentioned in the lectures, true recursion is not supported in macro expansion, we have to provide a fixed set of rules to handle the different sequences of substitutions we wish to perform. Fortunately, the dimento specs say the number of parameters supported is strictly 1 to 6, so a fixed rule set is possible.
The general layout might follow the style below
/* rules to handle a specific number of args, 1-3, where at each step we process one argument then "recursing" for the rest, PROCESS_ARGi would be replaced with whatever you want to do to that one argument The initial call from processAll makes sure it starts with the correct doArgsi */ #define doArgs1(arg1 ) PROCESS_LAST #define doArgs2(arg2, ...) PROCESS_SECOND_TO_LAST, doArgs1(__VA_ARGS__) #define doArgs3(arg3, ...) PROCESS_THIRD_TO_LAST, doArgs2(__VA_ARGS__) / * count the number of arguments in a list, e.g. COUNT(x,y,z) works on up to three arguments Explanation: The actual arguments passed match up with positions _1, _2, etc, which are discarded (that's what the _x means in C macros). Then (when it runs out of passed arguments) the values 3, 2, 1, 0 match up with the remaining _i's to discard in RESOLVE_COUNT. The value that matches up with the N in RESOLVE_COUNT is the number of arguments that were in the original list! */ #define RESOLVE_COUNT(_1, _2, _3, N, ...) N #define COUNT(...) RESOLVE_COUNT(__VA_ARGS__, 3, 2, 1, 0) /* given a count of the number of args, build the name of the right doArgs variant and call it */ #define processCall(count, ...) doArgs##count(__VA_ARGS__) #define processAll(count, ...) processCall(count, __VA_ARGS__) /* top level call to process a list of args #define process(...) processAll(COUNT(__VA_ARGS__), __VA_ARGS__) /* An example of how it would work, showing the sequence of expansions process(x,y) processAll(COUNT(x,y), x,y) processAll(RESOLVE_COUNT(x,y,3,2,1,0),x, y) processAll(2,x,y) processCall(2,x,y) doArgs2(x,y) PROCESS_SECOND_TO_LAST doArgs1(y) PROCESS_SECOND_TO_LAST PROCESS_LAST */
// example #define doArgs1(arg1 ) foo(arg1) #define doArgs2(arg2, ...) blah(arg2), doArgs1(__VA_ARGS__) #define doArgs3(arg3, ...) ick(arg3), doArgs2(__VA_ARGS__) #define RESOLVE_COUNT(_1, _2, _3, N, ...) N #define COUNT(...) RESOLVE_COUNT(__VA_ARGS__, 3, 2, 1, 0) #define processCall(count, ...) doArgs##count(__VA_ARGS__) #define processAll(count, ...) processCall(count, __VA_ARGS__) #define process(...) processAll(COUNT(__VA_ARGS__), __VA_ARGS__) process(x,y) // becomes blah(x), foo(y) |
Original dimento code |
#include "dimento.h" #include <math.h> func(root,a) { return sqrt(a); } func(f,a,b) { decl(x,y,z); x = a * a; y = b * b; z = eval(sqrt,x / y); return z; } func(product,a,b,c) { return a * b * c; } dimento_start decl(x); decl(y,z); x = 3; y = 35; z = eval(f,x,y); prt(z); prt(z * if(10,x,y,z)); decl(a,b,c); a = AND(x,z); prt(a); b = OR(x,z); prt(b); c = NOT(x); prt(c); a = test(b>c); prt(a); a = 5; while (a,true) { a--; prt(a); } dimento_end |
Expanded C code (minus the mass of #include stuff) |
float root( float a ) { return sqrt(a); } float f( float a, float b ) { float x,y,z ; x = a * a; y = b * b; z = sqrt(x / y); return z; } float product( float a, float b, float c ) { return a * b * c; } int main () { float x ; float y,z ; x = 3; y = 35; z = f(x,y); printf("%s = %g\n", "z", (z)); printf("%s = %g\n", "z * if(10,x,y,z)", (z * (((10)<0)?(x):(((10)>0)?(z):(y))))); float a,b,c ; a = (((x)<(z))?((((x)<0)?-1:((x)>0))):((((z)<0)?-1:((z)>0)))); printf("%s = %g\n", "a", (a)); b = (((x)>(z))?((((x)<0)?-1:((x)>0))):((((z)<0)?-1:((z)>0)))); printf("%s = %g\n", "b", (b)); c = (-((((x)<0)?-1:((x)>0)))); printf("%s = %g\n", "c", (c)); a = (((b>c)<0)?-1:((b>c)>0)); printf("%s = %g\n", "a", (a)); a = 5; while ((((a)<0)?-1:((a)>0)) == (1)) { a--; printf("%s = %g\n", "a", (a)); } } |
Output from running the C code |
z = 0.0857143 z * if(10,x,y,z) = 0.00734694 a = 1 b = 1 c = -1 a = 1 a = 4 a = 3 a = 2 a = 1 a = 0 |