Cell* op_floor::eval_op(Cell* operand) const { Cell* operand_ptr; no_of_operands(operand,1,1,true,true); operand_ptr = car(operand); if (listp(operand_ptr)) { operand_ptr = eval(operand_ptr); } else if (symbolp(operand_ptr)) { operand_ptr = search_symbol(get_symbol(operand_ptr),true); } if (doublep(operand_ptr)) { return make_int( int(floor(get_double(operand_ptr))) ); } else { if (operand_ptr != NULL) delete operand_ptr; throw runtime_error("'floor' only operates with double."); } }
Cell* op_plus::eval_op(Cell* operand) const { // keep adding until reaches nil pointer int int_sum = 0; double double_sum = 0.0; bool double_exist = false; //to store the result of car cell if it is a list Cell* operand_ptr; //check if operand is not empty while(!nullp(operand)) { if (nullp(car(operand))) { if (operand_ptr != NULL) delete operand_ptr; throw runtime_error("'+' can only deal with integer and double."); } else if (listp(car(operand))) { operand_ptr = eval(car(operand)); } else if (symbolp(car(operand))) { operand_ptr = search_symbol(get_symbol(car(operand)),true); } else { operand_ptr = car(operand); } if (intp(operand_ptr)) { int_sum += get_int(operand_ptr); } else if (doublep(operand_ptr)) { double_sum += get_double(operand_ptr); double_exist = true; } else { delete operand_ptr; throw runtime_error("'+' can only deal with integer and double."); } operand = cdr(operand); } if (double_exist) return make_double(double_sum+int_sum); else return make_int(int_sum); };
Cell* eval(Cell* const c) { // when root cell is empty, throw an error. judge_nil_cell(c,"begin"); // when root cell is a int or double cell, return a copy. if (intp(c) || doublep(c) || procedurep(c)) { return c -> deep_copy(); } //if c is a symbol, there are several situations //if c is in the local map, which means c is a procedurecell. //if c is in the global map, which menas c is defined as some other value. if (symbolp(c)) { string var = get_symbol(c); // first check if the symbol is defined at a local space // if it is, then return a copy of it. if (!my_stack.empty()) { if (my_stack.top().count(var)) { bstmap<string,Cell*> temp = my_stack.top(); if (nullp(temp[var])) return nil; return temp[var] -> deep_copy(); } } // then check if the symbol is defined in the global map if (symbol_table.count(var)) { if (nullp(symbol_table[var])) return nil; return symbol_table[var] -> deep_copy(); } throw symbol_undefined_error("the variable " + var + " is not defined in the map"); } // Then we know the 'c' cell must be a root of list Cell* oper_cell = NULL; string oper; if (listp(car(c))) { oper_cell = eval(car(c)); // oper cell should be the operator (primitive or procedure cell) // it cannot be an empty operator if (nullp(oper_cell)) { throw invalid_operator_error("you cannot use an empty operator"); } // if the first element of the expression is a list, the result evaluating it must be a procedure if (!procedurep(oper_cell)) { throw invalid_operator_error("cannot apply a value that is not a function"); } // then return a copy of this procedure cell if (procedurep(oper_cell)) { Cell* argu_list = nullp(cdr(c)) ? nil : cdr(c) -> deep_copy(); return oper_cell -> apply(argu_list); } } oper_cell = car(c) -> deep_copy(); // the operator cannot be a int or double if (!symbolp(oper_cell)) { throw invalid_operator_error("The input operator is not a procedure or primitive type"); } oper = get_symbol(oper_cell); /** * the ceiling operator * using oper_ceil(), which is a virtual function in Cell. */ if (oper == "ceiling") { judge_num_argu(c,oper); // check validation Cell* temp_cell = eval(car(cdr(c))); judge_nil_cell(temp_cell,oper); // check validation Cell* result = temp_cell -> oper_ceil(); delete oper_cell; delete temp_cell; return result; } /** * the floor operator * using oper_floor(), which is a virtual function in Cell. */ if (oper == "floor") { judge_num_argu(c,oper); // check validation Cell* temp_cell = eval(car(cdr(c))); judge_nil_cell(temp_cell,oper); //check validation Cell* result = temp_cell -> oper_floor(); delete oper_cell; delete temp_cell; return result; } /** * the add operator * using oper_add(), which is a virtual function in Cell. */ if (oper == "+") { // by "+" convention, when evaluate a single plus operator, return 0. if (nullp(cdr(c))) { delete oper_cell; return make_int(0); } int size = cdr(c) -> cons_size(); // use cell:result as a initial cell Cell* result = make_int(0); Cell* next_elem = cdr(c); for (int i=1; i<=size; i++) { Cell* next_temp = eval(car(next_elem)); judge_nil_cell(next_temp,oper); result = result -> oper_add(next_temp); delete next_temp; //delete temporary cell in every step. next_elem = cdr(next_elem); } delete oper_cell; return result; } /** * the minus operator * using oper_minus(), which is a virtual function in Cell. */ if (oper == "-") { judge_num_argu(c,oper); int size = cdr(c) -> cons_size(); Cell* result = eval(car(cdr(c))); judge_nil_cell(result,oper); // by "-" convention, when only one operand, return its inverse. if (size == 1) { result = result -> oper_minus(nil); delete oper_cell; return result; } Cell* next_elem = cdr(cdr(c)); for (int i=1; i<size; i++) { Cell* next_temp = eval(car(next_elem)); judge_nil_cell(next_temp,oper); result = result -> oper_minus(next_temp); delete next_temp; //delete temporary cell in every step. next_elem = cdr(next_elem); } delete oper_cell; return result; } /** * the multiply operator * using oper_multiply(), which is a virtual function in Cell. */ if (oper == "*") { if (nullp(cdr(c))) { delete oper_cell; return make_int(1); } int size = cdr(c) -> cons_size(); Cell* result = make_int(1); Cell* next_elem = cdr(c); for (int i=1; i<=size; i++) { Cell* next_temp = eval(car(next_elem)); judge_nil_cell(next_temp,oper); result = result -> oper_multiply(next_temp); delete next_temp; //delete temporary cell in every step. next_elem = cdr(next_elem); } delete oper_cell; return result; } /** * the division operator * using oper_divide(), which is a virtual function in Cell. */ if (oper == "/") { judge_num_argu(c,oper); int size = cdr(c) -> cons_size(); Cell* result = eval(car(cdr(c))); judge_nil_cell(result,oper); // by "/" convention, when only one operand, return its inverse. if (size == 1) { result = result -> oper_divide(nil); delete oper_cell; return result; } Cell* next_elem = cdr(cdr(c)); for (int i=1; i<size; i++) { Cell* next_temp = eval(car(next_elem)); judge_nil_cell(next_temp,oper); result = result -> oper_divide(next_temp); delete next_temp; //delete temporary cell on every step. next_elem = cdr(next_elem); } delete oper_cell; return result; } /** * the if operator * using oper_if(), which is a virtual function in Cell. */ if (oper == "if") { judge_num_argu(c,oper); // check validation int size = cdr(c) -> cons_size(); Cell* judge_cell = cdr(c); // the first operand after if Cell* judge_cell_temp = eval(car(judge_cell)); Cell* true_cell = cdr(judge_cell); // the second operand after if // if judge cell is a empty list, return true_cell if (nullp(judge_cell_temp)) { delete judge_cell_temp; delete oper_cell; return eval(car(true_cell)); } Cell* condition = judge_cell_temp -> oper_if(); // condition must be an IntCell from the implementation of Cell.cpp if (get_int(condition)) { Cell* true_cell_temp = eval(car(true_cell)); delete judge_cell_temp; delete oper_cell; return true_cell_temp; } else { Cell* false_cell = nil; // if there are only two operand, which is an undefined behavior // we will let it return the second operand if (size == 2) { false_cell = eval(car(true_cell)); } else { false_cell = eval(car(cdr(true_cell))); } delete judge_cell_temp; delete oper_cell; return false_cell; } } /** * the quote operator */ if (oper == "quote") { judge_num_argu(c,oper); // check validation delete oper_cell; if (nullp(car(cdr(c)))) return nil; return car(cdr(c)) -> deep_copy(); } /** * the cons operator * using cons() which is a function in cons.hpp. */ if (oper == "cons") { judge_num_argu(c,oper); // check validation Cell* car_new = eval(car(cdr(c))); Cell* cdr_new = eval(car(cdr(cdr(c)))); if (!listp(cdr_new)) { throw invalid_operator_error("cdr must either be nil or a conspair"); } Cell* result = cons(car_new, cdr_new); delete oper_cell; return result; } /** * the car operator * using car() which is a function in cons.hpp. */ if (oper == "car") { judge_num_argu(c,oper); Cell* temp = eval(car(cdr(c))); Cell* result = nullp(car(temp)) ? nil : car(temp) -> deep_copy(); delete temp; delete oper_cell; return result; } /** * the cdr operator * using cdr() which is a function in cons.hpp. */ if (oper == "cdr") { judge_num_argu(c,oper); Cell* temp = eval(car(cdr(c))); Cell* result = nullp(cdr(temp)) ? nil : cdr(temp) -> deep_copy(); delete temp; delete oper_cell; return result; } /** * the nullp operator * using nullp() which is a function in cons.hpp. */ if (oper == "nullp") { judge_num_argu(c,oper); Cell* temp_cell = eval(car(cdr(c))); if(nullp(temp_cell)) { delete temp_cell; delete oper_cell; return make_int(1); } else { delete temp_cell; delete oper_cell; return make_int(0); } } /** * the define operator * using map to record relation between string and cell */ if (oper == "define") { judge_num_argu(c,oper); Cell* key_cell = car(cdr(c)); Cell* next_cell = car(cdr(cdr(c))); if (!symbolp(key_cell)) { throw operate_on_nil_error("define operand must be a symbol"); } Cell* mapped_cell = eval(next_cell); string key = get_symbol(key_cell); if (symbol_table.count(key)) { throw invalid_operand_error("Cannot redefine a variable"); } symbol_table.insert(make_pair(key,mapped_cell)); delete oper_cell; return nil; } /** * the less operator * using oper_less(), which is a virtual function in Cell. */ if (oper == "<") { //by convention, it will return 1 when zero argument. if (nullp(cdr(c))) { return make_int(1); } int size = cdr(c) -> cons_size(); //when one argument, return itself. if (size == 1) { Cell* result = eval(car(cdr(c))); judge_nil_cell(result,oper); result = result -> oper_less(nil); delete oper_cell; return result; } Cell* this_elem = cdr(c); Cell* next_elem = cdr(cdr(c)); int condition = 1; for (int i=1; i<size; i++) { Cell* next_temp = eval(car(next_elem)); judge_nil_cell(next_temp,oper); Cell* this_temp = eval(car(this_elem)); judge_nil_cell(this_temp,oper); Cell* judge_cell = this_temp -> oper_less(next_temp); // if there is one pair such that 'this' bigger than 'next', condition will be zero condition *= get_int(judge_cell); // delete temporary cell delete judge_cell; delete this_temp; delete next_temp; this_elem = cdr(this_elem); next_elem = cdr(next_elem); } delete oper_cell; return make_int(condition); } /** * the not operator * using oper_not(), which is a virtual function in Cell. */ if (oper == "not") { judge_num_argu(c,oper); Cell* operand = eval(car(cdr(c))); if (nullp(operand)) { delete oper_cell; return make_int(0); } Cell* result = operand -> oper_not(); delete operand; delete oper_cell; return result; } /** * the print operator * print the result and return nil cell */ if (oper == "print") { judge_num_argu(c,oper); Cell* result = eval(car(cdr(c))); if (nullp(result)) { cout << "()" << endl; return nil; } cout << *result << endl; delete oper_cell; delete result; return nil; } /** * the eval operator * evaluate the result and return nil */ if (oper == "eval") { judge_num_argu(c,oper); Cell* expr = eval(car(cdr(c))); Cell* result = eval(expr); delete oper_cell; return result; } /** * the lambda operator * a new function */ if (oper == "lambda") { judge_num_argu(c,oper); Cell* formal_m = nullp(car(cdr(c))) ? nil : car(cdr(c)) -> deep_copy(); // since the formal part must be a symbol or list(can be empty) if (!listp(formal_m) && !symbolp(formal_m) && !nullp(formal_m)) { throw invalid_operand_error("the type of formal part should be list or symbol"); } Cell* body_m = nullp(cdr(cdr(c))) ? nil : cdr(cdr(c)) -> deep_copy(); delete oper_cell; return lambda(formal_m,body_m); } /** * the apply operator * followed by a function and a list of arguments */ if (oper == "apply") { judge_num_argu(c,oper); Cell* procedure = eval(car(cdr(c))); if (!procedurep(procedure)) { throw invalid_operator_error("cannot apply a value that is not a function"); } Cell* argu_list = eval(car(cdr(cdr(c)))); if (!listp(argu_list)) { throw invalid_operand_error("the second operand after apply function must be a list"); } Cell* result = procedure -> apply(argu_list); delete procedure; delete argu_list; delete oper_cell; return result; } /* * the let operator * which allow define local variable before function definition */ if (oper == "let") { judge_num_argu(c,oper); Cell* var_definition = car(cdr(c)); Cell* func_body = car(cdr(cdr(c))); bstmap<string,Cell*> local_map; // According to the specification, the variable definition part must be list if (!listp(var_definition)) { throw invalid_operand_error("In let function, the varible definition must be a list"); } int size = car(var_definition) -> cons_size(); for (int i=0; i<size; i++) { Cell* begin_cell = car(var_definition); if (!listp(begin_cell)) { throw invalid_operand_error("In let function, the varible definition must be a list"); } int sub_size = begin_cell -> cons_size(); // When you want to define a variable, you can only give a symbol and a value, so the size should be 2 if (sub_size != 2) { throw wrong_num_argu_error("When define local variable, the size of list must be 2"); } string key = get_symbol(car(begin_cell)); Cell* argu = car(cdr(begin_cell)); local_map.insert(make_pair(key,argu)); var_definition = cdr(var_definition); } my_stack.push(local_map); return eval(func_body); } // If the oper is not the above primitive operator, then check whether they are in the global map // If it is, follow the similar step declared above if (symbol_table.count(oper)) { if (procedurep(symbol_table[oper])) { Cell* procedure = symbol_table[oper] -> deep_copy(); Cell* argu_list = nullp(cdr(c)) ? nil : cdr(c) -> deep_copy(); Cell* result = procedure -> apply(argu_list); delete oper_cell; delete procedure; delete argu_list; return result; } } throw invalid_operator_error("this operator is invalid"); }
/** * \brief The evaluation function that calculates the parsed s-expression tree * \param c A constant pointer to a Cell instance, which is the root of the tree to be computed * \return A pointer to the Cell containing the final answer. */ Cell* eval(Cell* const c) { if (nullp(c)) { error_handler("s-expression invalid: root of tree is nil"); } if (intp(c) || doublep(c) || symbolp(c)){ return deep_copy(c); } if (listp(c)) { //get car of c //using eval will automatically evaluate the subtree, if car(c) is itself a conspair Cell* car_value = eval(car(c)); //if car is a symbol cell (it must, otherwise the eval() begins at wrong place) if (symbolp(car_value)){ //get the symbol //case 1: + if (get_symbol(car_value) == "+"){ //delete car_value as it's not needed any more delete car_value; //temporary sums variables double double_sum = 0; int int_sum = 0; bool sum_is_double = false; //Cell pointer to the current working cell Cell* current_cell = cdr(c); //iterate every cons pair until meet a nil cdr while (!nullp(current_cell)) { //current_cell is not nil, and it should be a conspair if (!listp(current_cell)) { error_handler("cdr must be nil or conspair"); } //pointer to the cell that contains the value to be added //here eval could be used against a conspair or a int/double cell Cell* value_cell = eval(car(current_cell)); //deal with value_cell, see if it's int or not if (intp(value_cell)) { if (sum_is_double) { double_sum += get_int(value_cell); } else { int_sum += get_int(value_cell); } } else if (doublep(value_cell)) { //if value_cell is not a double cell if (sum_is_double) { double_sum += get_double(value_cell); } else { //migrate int_sum to double_sum and do related clean-ups double_sum = int_sum; int_sum = 0; sum_is_double = true; double_sum += get_double(value_cell); } } else { if (!nullp(value_cell)) delete value_cell; error_handler("s-expression invalid: + operands invalid"); } if (!nullp(value_cell)) delete value_cell; //move current_cell forward; current_cell = cdr(current_cell); } return sum_is_double ? make_double(double_sum) : make_int(int_sum); } //case 2: ceiling else if (get_symbol(car_value) == "ceiling") { //delete car_value as it's no longer needed delete car_value; //current working cell Cell* current_cell = cdr(c); if (nullp(current_cell) || !listp(current_cell)) error_handler("s-expression invalid: invalid ceiling operand!"); if (!nullp(cdr(current_cell))) error_handler("s-expression invalid: ceiling on more than one operands"); //take the ceiling and return Cell* returned_value = eval(car(current_cell)); if (intp(returned_value)){ delete returned_value; error_handler("s-expression invalid: ceiling on integer!"); } else if (doublep(returned_value)){ int ceilinged_value = int(get_double(returned_value)); if (ceilinged_value < get_double(returned_value)) ++ceilinged_value; delete returned_value; return make_int(ceilinged_value); } else { if(!nullp(returned_value)) delete returned_value; error_handler("s-expression invalid: ceiling on symbol!"); } } //case 3: if else if (get_symbol(car_value) == "if") { //delete car_value as it's no longer needed delete car_value; //temporary Cell pointers; Cell* condition = cdr(c); if (nullp(condition) || !listp(condition)) error_handler("s-expression invalid: condition is not a conspair"); Cell* if_true = cdr(condition); if (nullp(if_true) || !listp(if_true)) error_handler("s-expression invalid: the true return value is not a cospair"); Cell* if_false = cdr(if_true); //directly return the second parameter if the third doesn't exist if (nullp(if_false)) { return eval(car(if_true)); } else { if (!nullp(cdr(if_false))) error_handler("s-expression invalid: if operator on more than three operands"); Cell* condition_cell = eval(car(condition)); bool flag = false; //retrieve values according to their types if (intp(condition_cell)){ flag = get_int(condition_cell) ? true : false; } else if (doublep(condition_cell)) { flag = get_double(condition_cell) ? true : false; } else if (symbolp(condition_cell)) { flag = get_symbol(condition_cell)!="" ? true : false; } else { if(!nullp(car_value)) delete condition_cell; error_handler("s-expression invalid: condition operand invalid to if"); } if(!nullp(car_value)) delete condition_cell; return flag ? eval(car(if_true)) : eval(car(if_false)); } } else { //delete car_value as it's no longer needed delete car_value; error_handler("s-expression invalid: operator not one of +, ceiling or if"); } } else { //delete car_value as it's no longer needed if(!nullp(car_value)) delete car_value; //value_car is not a symbol cell error_handler("s-expression invalid: the first element of the tree/subtree is not a proper operator"); } } }