#include #include #include #include #include #include "interpret.h" #include "parser.h" #include "icode.h" #include "stfunc.h" void ICodeInterpretator::initialize(const IProgram* icode) { assert(icode != 0); program = icode; currentCommand = icode->begin(); nameTableStack.clear(); //... nameTableStack.push_back(NameTable()); // Global name table nameTableStack.emplace(); // Add standard functions to global name table addStandardFunctionsToNameTable(nameTableStack.top()); stack.clear(); addressStack.clear(); } void ICodeInterpretator::execute(int& lastLineNumber) { while (currentCommand != program->end()) { if (currentCommand->line_number != 0) lastLineNumber = currentCommand->line_number; executeCommand(); } if (show_nametable) { printf("\n"); nameTableStack.print(); } } bool ICodeInterpretator::defineFunctionAddress( const Value& f, IProgram::const_iterator& functionAddress ) const { const Value* v = &f; while (v->isReference()) { v = v->reference(); } if (v->isFunction()) { const Label* l = v->labelPtr(); if (l == 0) { // For value of function type, label number == v->int_value l = label_table.findLabel(v->int_value); } assert(l != 0); functionAddress = l->location; return true; } else { throw InterpretatorException( "Call of non-function " + v->string_value ); } } void ICodeInterpretator::executeCommand() { assert(currentCommand != program->end()); int commandType = currentCommand->type; NameDef* name; switch (commandType) { case ICommand::assign: { Value value0, value1; stack.pop(value1); stack.pop(value0); if ( !value0.isLValue() ) { throw InterpretatorException( "Left side of assigment is not an l-value" ); } setValue(value0, value1); stack.push(value0); break; } case ICommand::assign_local: { Value value0, value1; stack.pop(value1); stack.pop(value0); if ( !value0.isLValue() ) { throw InterpretatorException( "Left side of assigment is not an l-value" ); } setLocalValue(value0, value1); stack.push(value0); break; } case ICommand::assign_ref: { Value value0, value1; stack.pop(value1); stack.pop(value0); assert(value0.isVariable()); setLocalReference(value0, value1); stack.push(value0); break; } case ICommand::int_const: { Value value; value.value_type = VALUE_TYPE_INT; value.int_value = currentCommand->int_value; stack.push(value); break; } case ICommand::double_const: { Value value; value.value_type = VALUE_TYPE_DOUBLE; value.double_value = currentCommand->double_value; stack.push(value); break; } case ICommand::string_const: { Value value; value.value_type = VALUE_TYPE_STRING; value.string_value = currentCommand->string_value; stack.push(value); break; } case ICommand::name: { // Value of name or lvalue Value v; v.value_type = VALUE_TYPE_VARIABLE; v.string_value = currentCommand->string_value; stack.push(v); break; } case ICommand::def_global: { assert(!nameTableStack.empty()); NameDef* n = nameTableStack.top(). addName(currentCommand->string_value); n->setGlobal(); break; } case ICommand::empty_array: { Value v; v.value_type = VALUE_TYPE_ARRAY; stack.push(v); break; } case ICommand::array_append: { Value v1; stack.pop(v1); Value& v0 = readValue(stack.top()); //... Value::array_append(v0, readValue(v1)); Value w1; assignValue(w1, v1); Value::array_append(v0, w1); break; } case ICommand::array_element: { Value a, idx; stack.pop(idx); stack.pop(a); Value index; readValue(idx).convertToInt(index); int n = index.int_value; Value& array = readValue(a); if (!array.isArray()) throw InterpretatorException("Illegal array expression"); Value arrayElem; arrayElem.value_type = VALUE_TYPE_ARRAY_ELEMENT; arrayElem.int_value = n; arrayElem.array_ptr = &(array.array_value); stack.push(arrayElem); break; } case ICommand::plus: { Value v0, v1, res; stack.pop(v1); stack.pop(v0); Value::add(readValue(v0), readValue(v1), res); stack.push(res); break; } case ICommand::minus: { Value v0, v1, res; stack.pop(v1); stack.pop(v0); Value::sub(readValue(v0), readValue(v1), res); stack.push(res); break; } case ICommand::mul: { Value v0, v1, res; stack.pop(v1); stack.pop(v0); Value::mul(readValue(v0), readValue(v1), res); stack.push(res); break; } case ICommand::div: { Value v0, v1, res; stack.pop(v1); stack.pop(v0); Value::div(readValue(v0), readValue(v1), res); stack.push(res); break; } case ICommand::mod: { Value v0, v1, res; stack.pop(v1); stack.pop(v0); Value::mod(readValue(v0), readValue(v1), res); stack.push(res); break; } case ICommand::power: { Value v0, v1, res; stack.pop(v1); stack.pop(v0); Value::power(readValue(v0), readValue(v1), res); stack.push(res); break; } case ICommand::uminus: { Value v0, res; stack.pop(v0); Value::negate(readValue(v0), res); stack.push(res); break; } case ICommand::cmp: { Value v0, v1, res; stack.pop(v1); stack.pop(v0); Value::compare(readValue(v0), readValue(v1), res); stack.push(res); break; } case ICommand::output: { Value v; stack.pop(v); readValue(v).print(); break; } case ICommand::input: { Value v; stack.pop(v); scan(v); break; } case ICommand::go_to: { const Label* label = currentCommand->goto_label_ptr; assert(label->number != 0); //... if (label->location == 0) //... throw InterpretatorException("Undefined label"); currentCommand = label->location; return; } case ICommand::if_goto: { Value v; stack.pop(v); assert(v.value_type == VALUE_TYPE_INT); int res = v.int_value; int cond = currentCommand->ext; if ( (cond == ICommand::eq && res == 0) || (cond == ICommand::ne && res != 0) || (cond == ICommand::gt && res > 0) || (cond == ICommand::lt && res < 0) || (cond == ICommand::ge && res >= 0) || (cond == ICommand::le && res <= 0) ) { const Label* label = currentCommand->goto_label_ptr; assert(label->number != 0); //... if (label->location == 0) //... throw InterpretatorException("Undefined label"); currentCommand = label->location; return; } break; } case ICommand::if_throw: { Value v; stack.pop(v); assert(v.value_type == VALUE_TYPE_INT); int res = v.int_value; int cond = currentCommand->ext; if ( (cond == ICommand::eq && res == 0) || (cond == ICommand::ne && res != 0) || (cond == ICommand::gt && res > 0) || (cond == ICommand::lt && res < 0) || (cond == ICommand::ge && res >= 0) || (cond == ICommand::le && res <= 0) ) { throw InterpretatorException( currentCommand->string_value.c_str() ); } break; } case ICommand::exch: { Value v0, v1; stack.pop(v1); stack.pop(v0); stack.push(v1); stack.push(v0); break; } case ICommand::function_def: { NameDef* n = nameTableStack.top(). addName(currentCommand->string_value); n->setFunction(true); n->label = currentCommand->label; n->label_ptr = currentCommand->label_ptr; n->value.value_type = VALUE_TYPE_FUNCTION; n->value.string_value = currentCommand->string_value; n->value.int_value = currentCommand->label; n->value.setLabelPtr(currentCommand->label_ptr); break; } case ICommand::function_call: { // Function name must be on top of stack Value f; stack.pop(f); const Value& ff = readValue(f); if (!(ff.isFunction() || ff.isStandardFunction())) throw InterpretatorException( "Call of non-function" ); ICommandPtr callAddress; if (ff.isFunction()) { if (defineFunctionAddress( ff, callAddress )) { ICommandPtr returnAddr = currentCommand; ++returnAddr; addressStack.push(returnAddr); // Call the function defined in the program currentCommand = callAddress; return; } else { // Should not come here! throw InterpretatorException( string("Unknown function ") + ff.string_value ); } } else { assert(ff.isStandardFunction()); // Check the table of standard functions //... const StdFuncDef* stfunc = findStandardFunction( //... ff.string_value //... ); const StdFuncDef* stfunc = ff.stdFuncPtr(); if (stfunc != 0) { // Check number of arguments Value numArgs; stack.pop(numArgs); if ( stfunc->numArgs >= 0 && numArgs.int_value != stfunc->numArgs ) { throw InterpretatorException( string("Illegal number of arguments of function ") + ff.string_value ); } int num_args = numArgs.int_value; vector argsPtr(num_args); vector args(num_args); if (stfunc->resIsLValue()) { // Result is the first argument of the function Value arg0; stack.pop(arg0); Value* res = &arg0; if (arg0.isLValue()) { res = &(readValue(arg0)); } args[0] = *res; argsPtr[0] = res; // Read all other arguments for (int i = 1; i < num_args; ++i) { Value arg; stack.pop(arg); args[i] = arg; argsPtr[i] = &readValue(args[i]); } stfunc->func(num_args, argsPtr.data(), res); stack.push(arg0); } else { // Read arguments for (int i = 0; i < num_args; ++i) { Value arg; stack.pop(arg); args[i] = arg; argsPtr[i] = &readValue(args[i]); } Value res; stfunc->func(num_args, argsPtr.data(), &res); stack.push(res); } } else { throw InterpretatorException( string("Unknown function ") + ff.string_value ); } } break; } case ICommand::function_entry: { //... nameTableStack.push_back(NameTable()); // local name table nameTableStack.emplace(); // local name table if (show_debug) { printf( "function_entry: %s\n", currentCommand->string_value.c_str() ); nameTableStack.print(); stack.print(); } break; } case ICommand::function_exit: { if (show_debug) { printf("function_exit (before pop):\n"); nameTableStack.print(); stack.print(); } nameTableStack.pop(); stack.push(Value(0)); // push 0 addressStack.pop(currentCommand); if (show_debug) { printf("function_exit (after pop):\n"); nameTableStack.print(); stack.print(); } return; } case ICommand::ret: { if (show_debug) { printf("function_exit (before pop):\n"); nameTableStack.print(); stack.print(); } if (stack.top().isLValue()) { // Replace a reference to an object on the top // of the stack by its value Value v = readValue(stack.top()); stack.pop(); stack.push(v); } nameTableStack.pop(); addressStack.pop(currentCommand); if (show_debug) { printf("function_exit (after pop):\n"); nameTableStack.print(); stack.print(); } return; } case ICommand::typecast: { Value value; stack.pop(value); const Value& v = readValue(value); Value res; if (currentCommand->int_value == TYPE_INT) { v.convertToInt(res); } else if (currentCommand->int_value == TYPE_DOUBLE) { v.convertToDouble(res); } else if (currentCommand->int_value == TYPE_STRING) { v.convertToString(res); } else { assert(false); res = v; } stack.push(res); break; } case ICommand::pop: stack.pop(); break; case ICommand::nop: // No operations break; default: { printf("Illegal interpreter command: type=%d\n", commandType); assert(false); } } ++currentCommand; } void ICodeInterpretator::printStackTop() const { if (stack.empty()) { printf("Stack empty"); return; } stack.top().print(); } // Fing a name either in local table or in enclosing tables NameDef* ICodeInterpretator::findName(const string& name) const { assert(nameTableStack.size() > 0); int i = int(nameTableStack.size() - 1); while (i >= 0) { NameDef* n = nameTableStack.at(i).findName(name); if (n != 0 && !n->isGlobal()) { return n; } --i; // Go to enclosing table } return 0; } NameDef* ICodeInterpretator::findNonlocalName(const string& name) const { assert(nameTableStack.size() > 1); if (nameTableStack.size() <= 1) return 0; int i = int(nameTableStack.size() - 2); while (i >= 0) { NameDef* n = nameTableStack.at(i).findName(name); if (n != 0 && !n->isGlobal()) { return n; } --i; // Go to enclosing table } return 0; } Value& ICodeInterpretator::getValue(const string& name) { NameDef* n = findName(name); if (n == 0) { return Value::none(); } if (n->value.isReference()) { Value* v = n->value.reference(); while (v->isReference()) { v = v->reference(); // Go to the enclosing name table } return *v; } else { return n->value; } } Value& ICodeInterpretator::getNonlocalValue(const string& name) { NameDef* n = findNonlocalName(name); if (n == 0) return Value::none(); return n->value; } // Set the value of name // If name is not global, then add a name to the local table, if necessary. void ICodeInterpretator::setValue(const string& name, Value& v) { assert(nameTableStack.size() > 0); NameDef* n = nameTableStack.top().findName(name); if (n != 0) { if (!n->isGlobal()) { if (n->isFunction()) throw InterpretatorException("Assignment to function name"); if (n->value.isReference()) { //... *(n->value.reference()) = readValue(v); Value* r = n->value.reference(); while (r->isReference()) { r = r->reference(); } //... *r = readValue(v); assignValue(*r, v); } else { //... n->value = readValue(v); assignValue(n->value, v); n->setVariable(); } } else { // Fing the name in enclosing tables n = findName(name); if (n == 0) throw InterpretatorException("Global name not found"); //... n->value = readValue(v); if (n->value.isReference()) { Value* r = n->value.reference(); while (r->isReference()) { r = r->reference(); } //... *r = readValue(v); assignValue(*r, v); } else { //... n->value = readValue(v); assignValue(n->value, v); } n->setVariable(); } } else { // Add the name to the local table NameDef* n = nameTableStack.top().addName(name); //... n->value = readValue(v); assignValue(n->value, v); n->setVariable(); } } void ICodeInterpretator::setLocalValue(const string& name, Value& v) { assert(nameTableStack.size() > 0); NameDef* n = nameTableStack.top().findName(name); if (n != 0) { n->value = readValue(v); n->clearModifiers(); n->setVariable(); } else { // Add the name to the local table NameDef* n = nameTableStack.top().addName(name); n->value = readValue(v); n->setVariable(); } } void ICodeInterpretator::setValue(const Value& lvalue, Value& v) { if (!(lvalue.isLValue() || lvalue.isReference())) throw InterpretatorException("Left side of assignment is not l-value"); assert(nameTableStack.size() > 0); if (lvalue.isReference()) { //... setValue(*lvalue.reference(), v); const Value* r = lvalue.reference(); while (r->isReference()) { r = r->reference(); } setValue(*r, v); } if (lvalue.isVariable()) { //... setValue(lvalue.string_value, readValue(v)); setValue(lvalue.string_value, v); // Sic! readValue is done in setValue } else if (lvalue.isArrayElement()) { assert(lvalue.array_ptr != 0); if ( lvalue.int_value < 0 || lvalue.int_value >= int(lvalue.array_ptr->size()) ) throw InterpretatorException("Array index is out of bounds"); //... lvalue.array_ptr->at(lvalue.int_value) = readValue(v); assignValue(lvalue.array_ptr->at(lvalue.int_value), v); } } void ICodeInterpretator::setLocalValue(const Value& lvalue, Value& v) { if (!lvalue.isLValue()) throw InterpretatorException("Left side of assignment is not l-value"); assert(nameTableStack.size() > 0); if (lvalue.isVariable()) { setLocalValue(lvalue.string_value, readValue(v)); } else if (lvalue.isArrayElement()) { assert(lvalue.array_ptr != 0); if ( lvalue.int_value < 0 || lvalue.int_value >= int(lvalue.array_value.size()) ) throw InterpretatorException("Array index is out of bounds"); lvalue.array_ptr->at(lvalue.int_value) = readValue(v); } } void ICodeInterpretator::setLocalReference(const Value& param, Value& v) { if (!param.isVariable()) // Must be a variable throw InterpretatorException("Incorrect function parameter"); assert(nameTableStack.size() > 1); if (!v.isLValue()) { setLocalValue( param.string_value, // Formal parameter name v // Value of actual parameter ); } else { NameDef* n = nameTableStack.top().findName(param.string_value); if (n != 0) throw InterpretatorException( "Incorrect list of function parameters" ); Value& w = readNonlocalValue(v); if (w.isNone()) throw InterpretatorException( "Undefined parameter" ); n = nameTableStack.top().addName(param.string_value); n->value.value_type = VALUE_TYPE_REFERENCE; n->value.setReference(&w); } } Value& ICodeInterpretator::readValue(Value& v) { // Read l-value if (!v.isLValue()) return v; if (v.isVariable()) { return getValue(v.string_value); } else if (v.isArrayElement()) { assert(v.array_ptr != 0); if ( v.int_value < 0 || v.int_value >= int(v.array_ptr->size()) ) throw InterpretatorException("Array index is out of bounds"); return v.array_ptr->at(v.int_value); } else if (v.isReference()) { return readValue(*v.reference()); } else { assert(false); } return Value::none(); // Should not come here } Value& ICodeInterpretator::readNonlocalValue(Value& v) { // Read l-value if (!v.isLValue()) return v; if (v.isVariable()) { return getNonlocalValue(v.string_value); } else if (v.isArrayElement()) { assert(v.array_ptr != 0); if ( v.int_value < 0 || v.int_value >= int(v.array_ptr->size()) ) throw InterpretatorException("Array index is out of bounds"); return v.array_ptr->at(v.int_value); } else { assert(false); } return Value::none(); // Should not come here } void ICodeInterpretator::assignValue(Value& lvalue, Value& v) { Value& w = readValue(v); if (!w.isNone()) { lvalue = w; } else { // Maybe, this is a standard function? if (v.isVariable()) { const StdFuncDef* stfunc = findStandardFunction(v.string_value); if (stfunc != 0) { lvalue.clear(); lvalue.value_type = VALUE_TYPE_STANDARD_FUNCTION; lvalue.string_value = v.string_value; return; } } throw InterpretatorException( "Undefined variable " + v.string_value ); } } void ICodeInterpretator::scan(Value& v) { if (!v.isLValue()) throw InterpretatorException("Incorrect input: no l-value"); // We always input a string char line[1024]; fgets(line, 1022, stdin); line[1022] = 0; int len = strlen(line); if (len > 0 && line[len-1] == '\n') { line[len-1] = 0; --len; } if (len > 0 && line[len-1] == '\r') { line[len-1] = 0; --len; } Value w(line); setValue(v, w); } void NameTableStack::print() const { printf("Name Table Stack, size=%d:\n", int(size())); int i = int(size() - 1); while (i >= 0) { printf("name table [%d]:\n", i); at(i).print(); printf("--------\n"); --i; } } void ExecStack::print() const { printf("Value Stack, size=%d:\n", int(size())); int i = int(size() - 1); while (i >= 0) { at(i).print(); printf("\n"); --i; } printf("--------\n"); }