CSCI 330 Final Exam Spring 2020 =============================== Preliminary sample solutions/outlines Question 1 ========== The key required parts are: 1. add optional params to constructor (&optional (H 0) (M 0)) 2. add print method in the labels defintions (print () (format t "~A:~A~%" hh mm)) 3. after the labels definitions and before start of the (lambda...), we need to initialize time from the constructor parameters, but need to error check those for validity, i.e. ...) ; end of labels method defs (set H M) (lambda ... start of dispatcher 4. add print command to dispatcher ((equalp cmd 'print) (print)) Question 2 ========== 1. (assuming all three of same type) template void rotate(T &x, T &y, T &z) { T tmp = x; x = y; y = z; z = tmp; } 2. (defmacro rotate (x y z) (let ((tmp (gensym))) (let ((,tmp ,x)) (setf ,x ,y) (setf ,y ,z) (setf ,z ,tmp)))) 3. C++ version is more intuitive to read and maintain C++ version guarantees all three are the same type If the macro/template is used frequently, then the set of generated C++ functions and calls might be smaller in object code than the collection of "inline" expansions generated by the lisp macro calls. 4. C++ version needs instantiation if compiled separately, lisp does not lisp version works on wider range of types (compatibility with setf vs =) lisp version doesn't involve a runtime function call Question 3 ========== The key required parts are: 1. need to add global variable in .lex, e.g. int count = 0; and access it as external in .yacc extern int count; 2. need to add "INTEGER" keyword/token in .lex, before TOK_IDENT, e.g. "INTEGER" { return(TOK_INTTYPE); } 3. in .lex, yywrap needs to print count, e.g. printf("Variables declared: %d\n", count); 4. in .yacc, need to add TOK_INTTYPE to list of tokens (%token...) 5. in .yacc, need to add grammar rules for variable declaration, and each variable declaration needs to increment count statement: vardecl; vardecl: TOK_INTTYPE TOK_IDENT TOK_SEMI { count++; } ** note that as an alternative, some folks incremented count in the .lex file each time they encountered the INTEGER keyword Question 4 ========== Using this format, you cannot do static type checking on parameters if you're using separately compiled files, since the .h doesn't contain information on the number/types of parameters (nor return type) Any issues will only become apparent when you attempt to link, since at that point one .o will contain calls that are not compatible with the function versions generated in the "callee" .o Similarly, this format is an issue for the developer, since they cannot necessarily look at the .h file to see the expected paramter profile unless the creator of the .h file (correctly) documented the number and types of expected parameters (and return type) This makes maintenance and modification much more error prone - possibly finding existing calls to the function and attempting to deduce the correct behaviour from those. Question 5 ========== In terms of associativity, the = and += should probably go right-to-left, to give sensible meaning to things like x = y = z; otherwise it would mean (x = y) = z; which would be assigning a value to the result of an expression, not to a variable. The +, on the other hand, should go left-to-right to be consistent with the conventional use of +. If the s?old:new operator is modifying s, then it is operating much like assignment, think of it like s = substitute(s,old,new), in which case it also likely needs to be evaluated right-to-left. The other operators could arguably use either form of associativity: left-to-right would probably be the assumed default, but I'll accept right-to-left if you have a decent justification. In terms of precedence, the =, +=, and ?: should probably have lower precedence than other operators, and should have equal precedence to each other, (i) so that statements of the form x = expr; follow the standard approach of evaluating the expression before assigning a value, otherwise things like x = y + z; would mean (x = y) + z; which does nothing with the result of the addition operation (ii) so that things like v += s?(w = x):y += y = z; still make sense (iii) so that things like s ? w + x / y : z also make sense The internal ranking of the other operators is debatable, and I'll accept any reasonable rationale for your choices. (Note that it is not enough to simply say something "should" be done in a specific order - you need to explain *why*.) Question 6 ========== (i) Because there is no dynamic memory allocation involved in the use of function pointers, dangling pointers and memory leaks would not be relevant. On the other hand, null and wild pointers could very probably lead to run time crashes/errors. (ii) One possible implementation of map: int *map(int (*fptr)(int), int *arr, int size) { // error check if (!fptr) return NULL; if (!arr) return NULL; if (size < 1) return NULL; // allocate and check int *result = (int*)(malloc)(size*sizeof(int)); if (!result) return NULL; // perform the actual mapping int i; for (i=0; i