CSCI 330: Spring 2020 Final Exam S20N01-02

Question Mark
1. let over lambda and OO
[10 marks]
 
2. macros in C++ and lisp
[10 marks]
 
3. grammars, lex, and yacc
[10 marks]
 
4. parameter handling
[10 marks]
 
5. precedence and associativity
[10 marks]
 
6. higher order functions
[10 marks]
 
7. loops and branching
[10 marks]
 
8. write your own exam question
[10 marks]
 
Exam Total

[80 marks]

 


Question 1: let over lambda and OO [10]

Below we provide a C++ class and an approximate let-over-lambda equivalent.

class mytime {
   private:
      int hh, mm;
   public:
      mytime() {
        hh = 0; mm = 0;
      }
      bool set(int h, int m);
      int gethh() { return hh; }
      int getmm() { return mm; }
};

bool mytime::set(int h, int m)
{
   if ((h >= 0) && (h < 60) &&
       (m >= 0) && (m < 60)) {
      hh = h;
      mm = m;
      return true;
   } else {
      return false;
   }
}
(defun mytime ()
   (let ((hh 0) (mm 0))
      (labels
          ( ; local functions
              (set (h m)
                  (if (and (>= h 0) (< h 60) (>= m 0) (< m 60))
                     (block 'oktime
                         (setf hh h)
                         (setf mm m)
                         t)))
              (gethh () hh)
              (getmm () mm))
          ; dispatcher
          (lambda (cmd &optional (arg1 0) (arg2 0))
              (cond
                 ((equalp cmd 'set) (set arg1 arg2))
                 ((equalp cmd 'gethh) (gethh))
                 ((equalp cmd 'getmm) (getmm))
                 (t (format t "bad command~%")))))))

Suppose we make the following changes/additions to the C++ class:

// change 1: adding optional parameters to the constructor
mytime(int h = 0, int m = 0) {
   hh = 0; mm = 0;
   set(h, m);
}

// change 2: adding a print method
void print() {
   cout << hh << ":" << mm << endl;
}

Provide equivalent changes/additions to the let-over-lambda.


Question 2: macros in C++ and lisp [10]

Macros in C++ are primarily based on the use of templates, whereas in lisp they are based on the defmacro.

  1. Provide a C++ templated function to rotate three values, i.e. rotate(x,y,z) effectively moves y's old value to x, z's old value to y, and x's old value to z.
  2. Provide a similar rotate macro for lisp.
  3. Briefly discuss the ways in which your C++ rotate macro is superior to your lisp rotate macro.
  4. Briefly discuss the ways in which your lisp rotate macro is superior to your C++ rotate macro.


Question 3: grammars, lex, and yacc [10]

Below we give lex and yacc files for the following grammar:

 /* Supported grammar:
  *    program --> stmtlist
  *    stmtlist --> statement stmtlist
  *             --> statement
  *    statement --> assignment
  *    assignment --> TOK_IDENT TOK_ASSIGN value TOK_SEMI
  *    value --> intexpr
  *    intexpr --> TOK_INT
  *
  */
Alpha [a-z]
Digit [0-9]

%{
#include 
#include "y.tab.h"
extern YYSTYPE yylval;
int yywrap();
int yyerror(char* s);
%}

%%

({Alpha})+ { return(TOK_IDENT); }
({Digit})+ { return(TOK_INT); }
";"        { return(TOK_SEMI); }
"="        { return(TOK_ASSIGN); }
[ \t\f\v\n]  { }
.          { char errmsg[] = "[c] bad char"; errmsg[1] = yytext[0]; yyerror(errmsg); return(yytext[0]); }

%%

int yywrap() {
   return 1;
}

int yyerror(char* s) {
   fprintf(stderr, "\n***Error: %s\n", s);
   return 1;
}
%{
#include
#include
int yylex(void);
int yywrap();
int yyerror(char* s);
%}

%start program
%token TOK_SEMI TOK_IDENT TOK_INT TOK_ASSIGN
%right TOK_ASSIGN

%%

program: stmtlist
	| error
	;

intexpr: TOK_INT
	;

value: intexpr
	;

assignment: TOK_IDENT TOK_ASSIGN value TOK_SEMI
	;

statement: assignment;
	;

stmtlist: statement stmtlist
	| statement
	;

%%

int main() {
   printf("Beginning parsing\n");
   int result = yyparse();
   printf("Done\n");
   return result;
}

Provide the necessary changes/additions to the file to handle the following changes/additions to the grammar:

new token:
   TOK_INTTYPE is the keyword "INTEGER"

new grammar rules:
   statement --> vardecl
   vardecl --> TOK_INTTYPE TOK_IDENT TOK_SEMI

new behaviour:
   the program counts how many variable declarations are made,
       and prints this count in function yywrap


Question 4: parameter handling [10]

Early implementations of C did not require a function prototype to specify return type or the number/types of parameters, e.g. the following was valid:

#include <stdio.h>

f();  /* prototype for function f */

int main()
{
   int x = 3;
   char y = 'q';
   float z = f(x,y);
   printf("%g\n", z);
}

float f(a, b) /* spec for function f */
int a;        /* spec for param a */
char b;       /* spec for param b */
{
   /* body of f, eventually returning a float */
}

This code can still be compiled and run, though the format is now regarded as obsolete. A header file containing a set of function definitions might thus look something like

#ifndef SOMENAME
#define SOMENAME

/* function prototypes */
foo();
blah();
anotherfoo();

#endif

Discuss the problems this could cause for developers and for compiler-writers, particularly in dealing with cases where a program consisted of multiple .c files, all seperately compiled into .o files before linking.


Question 5: precedence and associativity [10]

Suppose you were helping develop a new scripting language, and were given the opportunity to select both the associativity and precedence rules for a set of string operators:

(i) What levels/order of precedence would you choose for the operators and why?

(ii) Which of those operator(s) would you have evaluated left-to-right, and which would you have evaluated right-to-left, and why?


Question 6: higher order functions [10]

(i) Some of the pointer issues a C++ programmer has to deal with are dangling pointers, wild pointers, null pointers, and memory leaks. Of those four categories of pointer problem, which one(s) are relevant in the context of function pointers and why?

(ii) Suppose we wish to create a C function that operates (to a limited degree) like the lisp map function. It would take three parameters: a pointer to a function that takes an int as a parameter and returns an int, an array of ints, an int specifying the size of the array.

The map function would allocate an array of ints (the same size as the array passed), apply the passed function to each element of the array parameter, storing the result in the allocated array. When done, the map function would return the address of the allocated array, for example:
int square(int x) {
   return x * x;
}

int main() {
    int arr[3] = { 1, 2, 10 };
    int *result = map(square, arr, 3);
    // the array result points to should now contain 1, 4, 100
}

Provide an implementation of the map function.


Question 7: loops and branching [10]

(i) Argue both for and against the following statement:

(ii) If you were implementing a simplified form of the C language, and could only include one type of loop, which would it be and why?


Question 8: Write your own exam question [10]

Provide a new exam question and sample solution that would be an good addition to this CSCI 330 final exam, along with a justification of why you think it would have been a good question to include in the exam.

The criteria are as follows: