//
// An instruction interpreter for a simple assembly language
//
//
// A.B. Maccabe 9/6/99
//

#include <string>
#include <stdlib.h>
#include <iostream.h>
#include <ctype.h>
#include <vector.h>
#include <map.h>
#include <assert.h>


void inline skip_space( istream &in ) {
    while( !in.eof() && in.peek()!='\n' && isspace(in.peek()) ) {
	in.get();
    }
}

//
// A table of branch labels
map< string, int, less<string> > LabelTab;

//
// Storage for values
const int STORE_SIZE=100;
int Storage[STORE_SIZE];

class NameMap {
public:
    NameMap() {
	count = 0;
    }

    int add_name( string name ) {
	if( names.count( name ) == 0 ) {
	    names[name] = count++;
	    assert( count < STORE_SIZE );
	}
	return names[name];
    }

private:
    map< string, int, less<string> > names;
    int count;
} Names;


class Instruction {
public:
    // The constructor
    Instruction( string op="nop", string op1="0", string op2="0" ) {
	opnd1 = opnd2 = 0;
	target = "";

	// encode the operation
	for( oper=OP_INPUT ; oper<OP_INVALID && op!=op_name[oper] ; oper++ ) {
	    // Nothing
	}
	if( oper == OP_INVALID ) {
	    cerr << "Invalid operation -- " << op << endl;
	}

	// encode the operands
	switch( oper ) {
	case OP_INPUT:
	case OP_OUTPUT:
	    // one storage operand
	    opnd1 = Names.add_name( op1 );
	    break;
	case OP_COPY:
	case OP_MULT:
	case OP_DIV:
	case OP_ADD:
	case OP_SUB:
	    // two storage operands
	    opnd1 = Names.add_name( op1 );
	    opnd2 = Names.add_name( op2 );
	    break;
	case OP_EQ:
	case OP_NE:
	case OP_LT:
	case OP_LE:
	case OP_GE:
	case OP_GT:
	    // one storage operand and one target
	    opnd1 = Names.add_name( op1 );
	    target = op2;
	    break;
	case OP_SET:
	    // one integer and one storage operand
	    opnd1 = atoi(op1.c_str());
	    opnd2 = Names.add_name( op2 );
	    break;
	case OP_GOTO:
	    // one target
	    target = op1;
	    break;
	case OP_NOP:
	case OP_STOP:
	    // Nothing
	    break;
	}
    }

    // Execution of an instruction
    int execute( int PC ) {
	PC++;
	switch( oper ) {
	case OP_INPUT:
	    cin >> Storage[opnd1];
	    break;
	case OP_OUTPUT:
	    cout << Storage[opnd1] << endl;
	    break;
	case OP_COPY:
	    Storage[opnd2] = Storage[opnd1];
	    break;
	case OP_SET:
	    Storage[opnd2] = opnd1;
	    break;
	case OP_MULT:
	    Storage[opnd2] = Storage[opnd1] * Storage[opnd2];
	    break;
	case OP_DIV:
	    Storage[opnd2] = Storage[opnd1] / Storage[opnd2];
	    break;
	case OP_ADD:
	    Storage[opnd2] = Storage[opnd1] + Storage[opnd2];
	    break;
	case OP_SUB:
	    Storage[opnd2] = Storage[opnd1] - Storage[opnd2];
	    break;
	case OP_EQ:
	    if( Storage[opnd1] == 0 ) PC = branch();
	    break;
	case OP_NE:
	    if( Storage[opnd1] != 0 ) PC = branch();
	    break;
	case OP_LT:
	    if( Storage[opnd1] <  0 ) PC = branch();
	    break;
	case OP_LE:
	    if( Storage[opnd1] <= 0 ) PC = branch();
	    break;
	case OP_GE:
	    if( Storage[opnd1] >= 0 ) PC = branch();
	    break;
	case OP_GT:
	    if( Storage[opnd1] >  0 ) PC = branch();
	    break;
	case OP_GOTO:
	    PC = branch();
	    break;
	case OP_STOP:
	case OP_END:
	    cerr << "Program terminated normally" << endl;
	    PC = -1;
	    break;
	case OP_NOP:
	    break;
	}
	return PC;
    }

    int at_end() {
	return oper==OP_END;
    }

private:

    int branch() { 
	if( LabelTab.count( target ) == 0 ) {
	    cerr << "Branch to undefined label" << endl;
	    return -1;
	}
	return LabelTab[target];
    }

    enum op_code { 
	OP_INPUT, OP_OUTPUT,
	OP_COPY, OP_SET,
	OP_MULT, OP_DIV, OP_ADD, OP_SUB,
	OP_EQ, OP_NE,
	OP_LT, OP_LE,
	OP_GE, OP_GT,
	OP_GOTO,
	OP_STOP, OP_END,
	OP_NOP,
	OP_INVALID
    };

    static char* const op_name[] = {
	"input", "output",
	"copy", "set",
	"mult", "div", "add", "sub",
	"eq", "ne", 
	"lt", "le",
	"ge", "gt",
	"goto",
	"stop", "end",
	"nop"
    };
    
    int oper, opnd1, opnd2;
    string target;
};
char* const Instruction::op_name[];

int main( void ) {
    string oper;
    int line_no = 0;
    int instr_count = 0;
    Instruction last_instr;

    vector<Instruction> program;

    // read instructions
    while( !cin.eof() && !last_instr.at_end() ) {
	// starting a new line of input.....
	line_no++;

	if( cin.peek() != ';' ) {
	    bool have_oper = false;
	    while( !have_oper ) {
		oper = "";

		// skip leading white space
		skip_space( cin );
		
		// try to read the operator
		while( isalnum(cin.peek()) ) {
		    oper += cin.get();
		}
		skip_space( cin );

		// see if we read a label
		if( cin.peek() == ':' ) {
		    // we read a label -- add it to the symbol table
		    if( LabelTab.count( oper ) != 0 ) {
			cerr << "Redefinition of label \"" << oper << "\" ignored." 
			     << endl;
		    } else {
			LabelTab[oper] = instr_count;
		    }
		    cin.get();	// throw out the :
		} else {
		    have_oper = true;
		}
	    }

	    if ( oper != "" ) {
		// we've got an instruction, read the two operands
		string opnd1="", opnd2="";

		// read the first operand
		if( cin.peek() == '-' ) {
		    opnd1 += '-';
		    cin.get();
		}
		skip_space( cin );
		while( isalnum(cin.peek()) ) {
		    opnd1 += cin.get();
		}
		skip_space( cin );
		// read the second operand
		while( isalnum(cin.peek()) ) {
		    opnd2 += cin.get();
		}

		// Build an instruction and add it to the program
		last_instr = Instruction(oper,opnd1,opnd2);
		program.push_back( last_instr );
		instr_count++;
	    }
	}

	// throw out the rest of the line
	while( !cin.eof() && cin.get()!='\n' ) {
	    // Nothing
	}
    }

    // Execute the program starting at location 0;
    int PC = 0;

    while( PC >= 0 ) {
	PC = program[PC].execute( PC );
    }

    return 0;
}
