Type ::= int | bool | ^Type | Type * Type | (Type)where "int", "bool", "^", "*", "(" and ")" are terminal symbols.
Exp ::= Num | Ide | Exp Op Exp | let Ide = Num in Exp end | (Exp) Op ::= + | * | - | / Num ::= ... // sequence of digits representing a natural number Ide ::= ... // sequence of lettersand the interpreter eval for such language
int eval(tree* t, environment* r){ node* n = t->get_root(); string ty = n->get_type(); switch (ty) { case "num": return n->get_value(); case "ide": return r->lookup(n->get_ide()); case "op" : { int k1 = eval(t->get_left(), r); int k2 = eval(t->get_right(), r); switch (n->get_op()) { case "+": return k1 + k2; case "*": return k1 * k2; case "-": return k1 - k2; case "/": return k1 / k2; } } case "dec": { string x = n->get_ide(); int k = t->get_left()->get_root()->get_value(); environment* r1 = r->add(x,k); return eval(t->get_right(), r1); } } }Assume that we want to modify the language by adding negative expressions. More precisely, we want to add a unary operator "~", whose meaning is to change the sign of the value of the argument. For instance, if the value of x is 5, then the value of ~x should be -5. If the value of y is 2 and the value of z is 7, then the value of 5 * (~(~y + z)) should be -25. Show how the grammar and the eval function should be modified so to cope with such an extension.
Note: Such extension is in a sense redundant, because the value of ~e can be expressed already by writing 0 - e. But anyway, the exercise is realistic because most programming languages allow the unary minus operator.
int f(int <parameter passing-method> x){ x = x+1; y = y+2; return x + y; }where y is a global variable of type integer. Say what is the final value of y after the following fragment of code:
y = 0; y = f(y);under each of the following assumptions:
Exp ::= Exp implies Expand with a non-strict semantics of the following form:
the result of e1 implies e2 is - true if e1 is false - true if e1 is true and e2 is true - false if e1 is true and e2 is false
x > 0 implies (x = 1 or x > 1 or 1/x > 1)
procedure p(x:integer); var y:integer; /* declaration of a variable y local to p */ procedure q; /* declaration of a procedure q local to p */ procedure r; /* declaration of a procedure r local to q */ var x:integer; /* declaration of a variable x local to r */ begin /* begin body of r */ x := 0; q end; /* end body of r */ begin /* begin body of q */ if x = y then begin y := y - 1; r end else write(y) end; /* end body of q */ begin /* begin body of p */ y := x; q end; /* end body of p */Assume that at a certain point, the main program contains a call of the form
p(1);
class list{ int info; list* next; public: list(){ info = 0; next = NULL; } list(int x, list* l){ info = x; next = l; } ~list(){ delete next; } int length(){ //returns the length of the list int n = 0; list* l = this; while(l->next!=NULL){ l = l->next; n++; } return n; } }; list* makelist(){ //reads a sequence of integers and returns them in a list int x; list* l = new list(); cin >> x; while(x!=0){ l = new list(x,l); cin >> x; } return l; }For each of the following declarations of p, say whether a call to p leaves memory leaks and/or dangling references. (Note: "a call to p leaves ml/dr" means "after a call to p returns, we have ml/dr".)
void p(){ cout << makelist()->length(); }
void p(){ list* l = makelist(); cout << l->length(); l = NULL; }
void p(){ list* l = makelist(); cout << l->length(); delete(l); }
void p(){ list l = list(42,makelist()); cout << l.length(); }
void p(){ list l = list(42,makelist()); m = new list(53, &l); // m is a global variable of type list* cout << m->length(); }