2

I am currently working on a very basic assembler. The assembler needs to take in assembly instructions and output 16-bit binary instructions for use with a computer we are making.

My design strategy has been to create a Command class, that has 3 child classes. There is one for each type of command: A-commands, C-commands, and L-commands. To identify the type of command I am working with, I have included a string command_type that is either "A", "C", or "L" respectively.

EDIT:

I am still having a lot of trouble figuring out how to properly derive these classes. Basically, A and L commands should have a "symbol" string, which represents an integer value that needs to be converted, while C commands have "dest","comp", and "jump" values that also must be accessed, however they do not have "symbol" values.

Command.h

#include <fstream>
#include <string>

class Command {
    std::string command_type = "";
protected:
    void set_commandType(std::string x){command_type = x;}
public:
    Command();
    virtual ~Command();
    std::string commandType() const {return command_type;}

};
class A_COMMAND : public Command
{
    std::string symbol;
public:
    A_COMMAND(std::string s);
    std::string get_symbol(){return symbol;}; //Returns the symbol or decimal Xxx of the current command @Xxx or (Xxx) . Should be called only when commandType() is A_COMMAND or L_COMMAND.


};

class C_COMMAND : public Command
{
    std::string comp;
    std::string dest;
    std::string jump;
public:
    C_COMMAND(std::string s, std::string d, std::string j);
    std::string get_comp(){return comp;}; //Returns the comp mnemonic in the current C-command (28 possibilities). Should be called only when commandType() is C_COMMAND.
    std::string get_dest(){return dest;}; //Returns the dest mnemonic in the current C-command (8 possibilities). Should be called only when commandType() is C_COMMAND.
    std::string get_jump(){return jump;}; //Returns the jump mnemonic in the current C-command (8 possibilities). Should be called only when commandType() is C_COMMAND.
};

class L_COMMAND : public Command
{
    std::string symbol;
public:
    L_COMMAND(std::string s);
    std::string get_symbol(){return symbol;}; //Returns the symbol or decimal Xxx of the current command @Xxx or (Xxx) . Should be called only when commandType() is A_COMMAND or L_COMMAND.
};

Command.cpp

#include "Command.h"

//---------------------------------------------
//A-Command functions

Command::Command(){}

A_COMMAND::A_COMMAND(std::string s) : symbol(s)
{
    set_commandType("A");
}


//---------------------------------------------
//C-Command functions

C_COMMAND::C_COMMAND(std::string c, std::string d, std::string j) : comp(c), dest(d), jump(j)
{
    set_commandType("C");
}

//---------------------------------------------
//L-Command functions

L_COMMAND::L_COMMAND(std::string s) : symbol(s)
{
    set_commandType("L");
}

I have a Parser.cpp and Parser.h that process the input and are responsible for creating a deque of commands:

Parser.h

#include "Command.h"
#include <vector>
#include <deque>


class Parser {
private:
    std::deque<Command> commands;
public:
    Parser(std::vector<std::string>);
    bool hasMoreCommands() //are there more commands in the input?
    {
        if(commands.size() != 0)
            return true;
        else
            return false;
    }
    void advance(){commands.pop_front();} //move to next command, should only work if hasMoreCommands returns false}
    Command currentCommand(){return commands.front();}
    std::vector<std::string> translateCommands(); //convert commands into binary strings

};

Parser.cpp

#include "Parser.h"
#include "Command.h"
#include <vector>
#include <iostream>
#include <string>
#include <unordered_map>

bool inList(std::string& str, std::vector<std::string> list) //check if a given string contains one of the elements in the comp, dest, jump vectors. if so extract string for use in constructor
{
    for(auto i = list.begin(); i!=list.end(); ++i)
    {
        std::size_t found = str.find(*i);
        if(found!=std::string::npos)
        {
            return true;
        }
    }
    return false;
}


Parser::Parser(std::vector<std::string> input) {
    std::vector<std::string> dest_list = {"","M","D","MD","A","AM","AD","AMD"}; //all possible dests
    std::vector<std::string> comp_list = {"0","1","D","A","!D","!A","-D","-A","D+1","A+1","D-1","A-1","D+A","D-A","A-D","D&A","D|A","M","!M","-M","M+1","M-1","D+M","D-M","M-D","D&M","D|M"}; //all possible comps
    std::vector<std::string> jump_list = {"","JGT","JEQ","JGE","JLT","JNE","JLE","JMP"}; //all possible jumps
    std::string dest, comp, jump;
    std::deque<Command> commands;
    for(std::vector<std::string>::const_iterator i = input.begin(); i != input.end(); ++i)
    {
        std::string line = *i;
        if(*line.begin()=='@') //A-command
        {
            A_COMMAND command(line.substr(1));
            std::cout << "Command type: " << command.commandType() << "\n";
            std::cout << "symbol: " << command.get_symbol() << "\n";
            commands.push_back(command);
        }
        else if(*line.begin()=='(' && *line.rbegin() == ')' && line.size() > 2) //L-command
        {
            L_COMMAND command(line.substr(1, line.size() - 2));
            std::cout << "Command type: " << command.commandType() << "\n";
            std::cout << "symbol: " << command.get_symbol() << "\n";
            commands.push_back(command);        }
        else
        {
            std::string rhs = line;
            std::string dest_string = "";
            std::string comp_string = "";
            std::string jump_string = "";
            size_t equals_pos = line.find('='); //position of = in string, if present
            size_t semi_pos = line.find(';');   //position of ; in string, if present
             if(equals_pos != line.npos) //if there is an = then we have a dest
             {
                 dest_string = line.substr(0,equals_pos);
                 rhs = line.substr(equals_pos+1);
             }
             if(semi_pos != line.npos) //jump
             {
                 comp_string = rhs.substr(0,semi_pos);
                 jump_string = rhs.substr(semi_pos+1);
             }
             else //no jump
             {
                 comp_string = rhs;
             }

             //now confirm if inputs are valid
             if(inList(dest_string, dest_list))
                 dest = dest_string;
             else
                 std::cout << "invalid dest \n";
             if(inList(comp_string, comp_list))
                 comp = comp_string;
             else
                 std::cout << "invalid comp \n";
             if(inList(jump_string, jump_list))
                 jump = jump_string;
             else
                 std::cout << "invalid jump \n";

             C_COMMAND command(comp, dest, jump);
             std::cout << "Command type: " << command.commandType() << "\n";
             std::cout << "dest: " << command.get_dest() << "\n";
             std::cout << "comp: " << command.get_comp() << "\n";
             std::cout << "jump: " << command.get_jump() << "\n";
             commands.push_back(command);
        }
    }
}

My main.cpp loads the input, and passes it through the parser. The problem I have is that I cannot do anything with the input.

I have tried to write a function like so:

string translateLine(Command command, Code code) //Code is a table for translating the command
{
    string output;
    if(command.commandType() == "A")
    {
        string symbol = parser.currentCommand().get_symbol();
        cout << symbol << endl;
        //perform binary conversion
    }
    /*else if(command.commandType() == "C")
    {
        string dest = command.get_dest();
    }*/
     //shouldn't be any L commands in symbol-less version
    else
    {
        std::cout << "unexpected command value \n";
    }
    return output;
}

But as soon as I call get_symbol(), the compiler doesn't recognize the function. I know that this is because the base Command doesn't have a get_symbol() function, but I can't figure out how to correctly add the functions to the base class and derive them to the lower 3. I can't just make the pure virtual because not all of the functions are used in each class. How can I correctly accomplish this?

  • 1
    There's nothing wrong with the code you've posted. Therefore, the problem will be with the code you have not posted. You need to edit your question, and include a [mcve]. – Sam Varshavchik Mar 27 '16 at 20:55
  • Do you really want set_commandType() to be a public function? If not, consider making the `command_type` protected. Do you want the derived classes to be able to change their type while they run? If not, keep it private and make the value an argument to a protected constructor that only classes derived from Command can use. *(If there's no sensible way to speak of a command with no type, that can be for the best...to avoid people creating Command instances.)* – HostileFork says dont trust SE Mar 27 '16 at 21:00
  • So the original example should work? Interesting, eclipse is complaining about these declarations. I will edit my original question to provide more information. – Stalemate Of Tuning Mar 27 '16 at 21:00
  • Hmm I think it must have been a linker problem. I neglected to include "Command.h" in one of my parser files. Thanks for the help anyway, sorry to waste your time. – Stalemate Of Tuning Mar 27 '16 at 21:09
  • I'm having a new problem and I have added more information, hopefully this helps. I'm not sure if this constitutes asking a new question or not, but I think it's the same sort of issue. – Stalemate Of Tuning Mar 27 '16 at 23:22

2 Answers2

2

First, if translateLine() should be able to accept A_COMMAND, C_COMMAND, or L_COMMAND objects, then it needs to take a Command* parameter, not a Command parameter. A pointer to a base class can hold a pointer to a class derived from that base, but an object of the base class cannot hold a derived object.

Second, you cannot call a function that belongs to A_COMMAND even with a Command pointer that is really pointing to a A_COMMAND object without doing a dynamic_cast. A dynamic_cast can convert a pointer from Command* to A_COMMAND at run-time and will return NULL if the object pointed to is not really an A_COMMAND object.

Kyle A
  • 928
  • 7
  • 17
  • Thanks! I will give this a try. – Stalemate Of Tuning Mar 27 '16 at 23:52
  • An iterator should work for this right? If so, how would a dynamic cast on such an iterator look? – Stalemate Of Tuning Mar 28 '16 at 00:23
  • He could also use references instead of pointers if he so desires, with the catch that `dynamic_cast` would throw an exception instead of returning `NULL`/`nullptr` in case of failure.. – Justin Time - Reinstate Monica Mar 28 '16 at 01:52
  • @Araganor, I think an iterator would probably work, because most iterators are wrappers around pointers, so you would need to get the iterator to expose the pointer in order to do the dynamic cast. – Kyle A Mar 28 '16 at 11:50
  • @Justin Time, that's an interesting technique. I've never tried to dynamic_cast a reference before. – Kyle A Mar 28 '16 at 11:50
  • @KyleA Nor have I, but it's mentioned in the standard. See http://stackoverflow.com/a/1276873/5386374 for the relevant quote, and part of the reasoning behind it (the other part, as far as I'm aware, being that since a reference can't be `NULL`/`nullptr`, casting a reference logically needs to have a different failure behaviour). – Justin Time - Reinstate Monica Mar 31 '16 at 18:14
0

The basic problem is that you're reinventing Run-Time Type information. You don't need to add "command_type=A" to class A_COMMAND. C++ already knows what types your objects have, if there's at least one virtual method. And it looks like your class needs a virtual destructor.

MSalters
  • 173,980
  • 10
  • 155
  • 350