Interpretador - Máquina von Neumann - C++

Iniciado por luizvd, 18 de Maio de 2007, 09:45

tópico anterior - próximo tópico

luizvd

Olá pessoal, tudo bem?

Por favor, veja se alguém de vocês pode me ajudar.

É o seguinte, preciso implementar uma máquina de von Neumann para 2 registradores em C++. Para isso preciso criar um pequeno interpretador que leia o código da máquina, identifique e execute as funções corretamente e me dê o resultado final.

O código da máquina de von Neumann é basicamente isso:


leiaA
ate (azero)
faca (subtraiA, adicionaB, adicionaB)
mostraB


Esse código fica em um arquivo separado. Toda a estrutura do programa básica (abrir o arquivo, ler o arquivo, armazenar cada linha em um vetor, etc) está pronta. Estou perdido mesmo em como identificar e executar cada uma das funções.

Não estou pedindo nenhum script pronto, apenas um auxílio (ou melhor, uma luz) para como fazer isso.

Muito obrigado desde já.

[ ]'s

Diego_Rocha

não entendi muito bem o que tu que, tu podia postar o codigo que tu ja fez pra gente poder te ajudar ai fica mais, t++

solanoalves

Diego deve ser matéria de faculdade por isso fica difícil entender o que ele quer ou é mal explicado mesmo hehehehe.

Diego_Rocha

E ai cara, hoje sem nada pra fazer, revendo os topicos decidi criar um programinha que interpreta algumas coisas, ta bem simples mas acho que serve como base pra tu, nao sei se era bem isso que tu procurava mas ta ai,

vonneumann.h

#ifndef _VONNEUMANN_H
#define _VONNEUMANN_H

#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <fstream>

class Compilador
{
private:
std::string MyString;
std::string VariavelStr;

protected:
std::map<std::string, std::string> MyVectorStr;

public:
Compilador(std::string MyFileStr);
virtual ~Compilador();
};

Compilador::Compilador ( std::string MyFileStr )
{
std::ifstream file;
file.open( MyFileStr.c_str(), std::ifstream::in );
while( file >> MyString ) {

if( MyString == "cria" ) {
file >> MyString;
VariavelStr = MyString;
file >> MyString;
if ( MyString.c_str() == "=" ){
file >> MyString;
MyVectorStr[VariavelStr] = MyString;
}
else
{
MyVectorStr[VariavelStr];
}
}
else if ( MyString == "imprimi" )
{
file >> MyString;
std::cout<< MyVectorStr[MyString];
}
else
{
std::cout<< MyString<< std::endl;
}
}
}

Compilador::~Compilador()
{

}
#endif


vonneumann.cpp

#include "vonneuman.h"

using namespace std;

int main(int argc, char **argv)
{
if ( argc == 2 ){
Compilador compila( argv[1] );
}else{
cout <<"Erro falta de parametros"<< endl;
}
return 0;
}


exemplo de uso

arquivo_de_teste.txt

cria variavel = minha_primeira_varivel
cria variavel1 = minha_segunda_variavel
cria variavel2 = minha_terceira_variavel

imprimi variavel
imprimi variavel1
imprimi variavel2

cria variavel3 = minha_quarta_variavel
imprimi variavel3


resultado:

./teste arquivo_de_teste.txt
minha_primeira_varivel
minha_segunda_variavel
minha_terceira_variavel
minha_quarta_variavel


agora vc tendo isso como base pode criar mais palavras chaves para simular um if, um for etc; se era uma ideia que tu queria ta ai naum sei se vai te servir mais é isso ai t++

luizvd

Olá pessoal, desculpem a demora em reaparecer aqui, mas estive meio ocupado com algumas coisas.

E desculpem se não fui muito claro no que precisava, às vezes na pressa eu posso ter me confundido (e confundido vocês) hehehehe.

Eu entreguei o trabalho ontem, e após quebrar muito a cabeça, consegui fazer o que precisava. Segue o código para quem quiser dar uma olhada. Se tiverem sugestões ou críticas ao código, sintam-se livres para fazê-los. Assim aprendo um pouco mais e vamos discutindo o assunto ;)

Código a ser interpretado:

leia_A
ate (a_zero)
faca (subtrai_A, adiciona_B, adiciona_B)
mostra_B



main()

/*
*  interpretador.cpp
*
*  Projeto: Interpretador
*  Interpretador em C++ para máquina de von Neumann
*  com 2 registradores
*
*  Compilado com:
*  $ gcc --version
*  gcc (GCC) 4.1.2 (Ubuntu 4.1.2-0ubuntu4)
*
*  Em:
*  Ubuntu 7.04 Feisty Fawn
*  Linux pc-luiz 2.6.20-15-generic #2 SMP i686
*
*  Luiz Vitor Damim
*  cod: xxxxxxxx
*  2007/05/22 17:36:35
*/
#include <iostream>
#include <string>
#include <vector>
#include <map>
using namespace std;

/*
* Definindo a macro _clrscr()
* Alterar conforme necessário se for compilar
* em outro sistema operacional
*/
#define _clrscr() system("clear");


#include "maquina.h"
/*
* Criando um tipo ponteiro-para-função-membro usado para mapear
* um map no formato {comando da máquina} => {comando da classe Maquina}
*
* Pesquisado em:
* http://www.parashift.com/c++-faq-lite/pointers-to-members.html#faq-33.5
*/
typedef void (Maquina::*MaquinaMemberFn)();
#define CALL_MEMBER_FN(object, ptrToMember) ((object).*(ptrToMember))

#include "parser.h"
#include "interpretador.h"

int main()
{
    Interpretador interpret = Interpretador();
    interpret.run();

    return 0;
}



interpretador.h

/**
* Class: Interpretador
* Recebe o nome do arquivo, abre e manda cada linha para o Parser
* para execução
*
* @see Parser::parse()
*/
class Interpretador
{
    private:
        int _arquivoNaoEncontradoCounter; /* contador de tentativas de abertura
                                             do arquivo */
        char _buffer[100];                /* buffer de leitura do arquivo */
        string _arquivo;                  /* nome do arquivo */
        vector<string> _programa;         /* vetor contendo as linhas de
                                             execução */
        FILE* _fp;                        /* ponteiro para o arquivo */
        Parser* _parser;

        /**
         * Pergunta o nome do arquivo para o usuário e tenta abrí-lo por 3 vezes
         * (somente se já não foi informado durante o construtor)
         *
         * Se não conseguir (porque não foi encontrado ou por não ter
         * permissão), termina a execução do programa
         *
         * @return void
         */
        void _abrirArquivo();

        /**
         * Método proxy para Parser::parse()
         * Itera sobre o vetor contendo o código da máquina
         * e manda cada linha para o analisador
         *
         * @see Parser::parse()
         * @return void
         */
        void _parse();


    public:
        Interpretador();
        Interpretador(char* arquivo);

        ~Interpretador();


        /**
         * Executa o interpretador
         *
         * @return void
         */
        void run();
};



/* ********************************************* */

inline Interpretador::Interpretador()
    : _arquivoNaoEncontradoCounter(3)
{ }


inline Interpretador::Interpretador(char* arquivo)
    : _arquivoNaoEncontradoCounter(3),
      _arquivo                        (arquivo)
{ }


inline Interpretador::~Interpretador() { this->_programa.clear(); }


inline void Interpretador::_abrirArquivo()
{
    do {
        if (this->_arquivo.empty()) {
            cout<< "Digite o nome do arquivo: " << endl;
            cin>> _arquivo;
        }

        this->_fp = fopen(this->_arquivo.c_str(), "r");

        if (this->_fp == false) {
            this->_arquivoNaoEncontradoCounter--;
            this->_arquivo.clear();
            _clrscr();

            if (this->_arquivoNaoEncontradoCounter == 0) {
                cout<< "Terminando execução do aplicativo." << endl << endl;
                exit (0);
            }

            cout<< "Arquivo nao existe. ";
            cout<< "Tentativas restantes: "
                << this->_arquivoNaoEncontradoCounter << endl;
        }
    }
    while (this->_fp == false);

    /*
     * OK, arquivo aberto
     * agora vamos ler e armazenar o conteúdo no vetor
     */
    while (!feof(this->_fp)) {
        fgets(this->_buffer, 100, this->_fp);
        this->_programa.push_back(this->_buffer);
    }
    /*
     * Por algum mistério misterioso do oculto desconhecido,
     * a última linha está sendo duplicada... vamos removê-la
     */
    this->_programa.pop_back();

    fclose(this->_fp);
    _clrscr();
}


inline void Interpretador::_parse()
{
    this->_parser = new Parser();
    vector<string>::iterator iter;

    for (iter  = this->_programa.begin();
         iter != this->_programa.end();
         iter++)
    {
        this->_parser->parse(*iter);
    }
}


inline void Interpretador::run()
{
    this->_abrirArquivo();
    this->_parse();
}


parser.h

/**
* Class: Parser
* Identifica e analisa as instruções da máquina de 2 registradores, e
* as executa adequadamente
*/
class Parser
{
    private:
        bool _loop;                      /* flag indicando o estado de loop */
        string _condicaoLoop;            /* condição de teste do loop */
        vector<string> _comandosLoop;    /* vetor contendo os comandos
                                            executados enquanto em loop */

        Maquina* _maquina;                        /* instância da Máquina */
        string _instrucao;                        /* próxima instrução a ser
                                                     executada */
        map<string, MaquinaMemberFn> _mapFuncoes; /* mapeamento das funções da
                                                     máquina de 2 registradores
                                                     com a classe Máquina */


        /**
         * Executa a próxima função identificada e armazenada em
         * Maquina::_instrucao
         *
         * @return void
         */
        void _execute();


    public:
        Parser();

        ~Parser();


        /**
         * Esta função recebe linha a linha as instruções da máquina de
         * 2 registradores, analisa a linha, separa as instruções e
         * as executa
         *
         * @param string linha
         * @return void
         */
        void parse(string linha);
};



/* ********************************************* */

inline Parser::Parser()
    : _loop(false),
      _maquina(new Maquina())
{
    /*
     * Mapeando as instruções da máquina de 2 registradores
     * com o endereço de memória das funções reais
     * da classe Máquina
     *
     * Apesar de passar o endereço de memória da classe
     * e não da instância, os métodos são executados corretamente
     * pois quando CALL_MEMBER_FN é chamado, o primeiro parâmetro
     * é a instância real da classe
     */
    this->_mapFuncoes["leiaa"]     = &Maquina::leiaA;
    this->_mapFuncoes["subtraia"]  = &Maquina::subtraiA;
    this->_mapFuncoes["adicionab"] = &Maquina::adicionaB;
    this->_mapFuncoes["mostrab"]   = &Maquina::mostraB;
}

inline Parser::~Parser()
{
    this->_comandosLoop.clear();
    this->_mapFuncoes.clear();
}


inline void Parser::_execute()
{
    /*
     * Removendo o último caractere (provavelmente um \0)
     * porque com ele não estava funcionando
     */
    string instrucao = this->_instrucao.substr(0, this->_instrucao.length()-1);

    /*
     * verifica se a instrução está vazia
     * se não estiver, a executa
     */
    if (!instrucao.empty()) {
        CALL_MEMBER_FN(*this->_maquina, this->_mapFuncoes[instrucao])();
    }

    /*
     * Verifica se estamos em modo de loop e se o vetor contendo os
     * comandos não está vazio
     */
    if ((this->_loop == true) && (this->_comandosLoop.empty() == false)) {
        while (!this->_maquina->testaA()) { // até (a_zero)
            vector<string>::iterator iter;
            for (iter  = this->_comandosLoop.begin();
                 iter != this->_comandosLoop.end();
                 iter++)
            {
                // Executando a instrução
                CALL_MEMBER_FN(*this->_maquina, this->_mapFuncoes[*iter])();
            }
        }

        /*
         * Após tudo executado, limpa o vetor e sai
         * do modo de loop
         */
        this->_comandosLoop.clear();
        this->_loop = false;
    }
}


inline void Parser::parse(string linha)
{
    // Limpa a instrução caso ela ainda tenha algo armazenado
    this->_instrucao.clear();

    unsigned int tamanho = linha.size()-1;
    for (unsigned int i = 0; i <= tamanho; i++)
    {
        char ch = linha[i];

        /*
         * Ignora os caracteres "_" [underline] e " " [espaço]
         *
         * [underline]: para evitar erros na hora da "programação"
         *              da máquina. Dessa forma, os comandos leia_A,
         *              leiaA, leia_a e leiaa são todos interpretados
         *              corretamente.
         *
         * [espaço]: já ignoro e removo os espaços aqui para
         *           não precisar identificá-los e tratá-los
         *           mais para frente
         */
        if (ch == '_' || ch == ' ')
            continue;


        if (this->_instrucao == "ate")
        {
            if (ch == '(')
                continue;

            if (ch == ')') {
                this->_loop = true; // entrando em modo loop
                this->_instrucao.clear();
                continue;
            }

            this->_condicaoLoop += ch; // armazenando a condição
                                       // do loop: a_zero
            continue;
        }


        if (this->_loop == true) {
            if (this->_instrucao == "faca")
                this->_instrucao.clear();

            if (ch == '(')
                continue;

            if (ch == ')' || ch == ',') {
                // adicionando os comandos do loop no vetor
                // para execução
                this->_comandosLoop.push_back(this->_instrucao);
                this->_instrucao.clear();
                continue;
            }
        }

        /*
         * transformando os caracteres em minúsculo para
         * facilitar a identificação e corrigir as variações
         * da máquina de 2 registradores
         */
        this->_instrucao += tolower(ch);
    }

    // Execta a instrução
    this->_execute();
}


maquina.h

/**
* Class: Maquina
* Máquina de 2 registradores de von Neumann
* (é esse o nome do tizão né?)
*
* contém as funções leiaA(), testaA(), subtraiA(), adicionaB(), mostraB()
*/
class Maquina
{
    private:
        int A, B; // registradores


    public:
        Maquina();

        /**
         * leia_A
         * Recebe o valor de A
         *
         * @return void
         */
        void leiaA();

        /**
         * a_zero
         * Testa se A <= 0
         *
         * @return true
         */
        bool testaA();

        /**
         * subtrai_A
         * Subtrai A uma vez enquanto Maquina::testaA() for verdadeiro
         *
         * @see Maquina::testaA()
         * @return void
         */
        void subtraiA();

        /**
         * adiciona_B
         * Adiciona B uma vez enquanto Maquina::testaA() for verdadeiro
         *
         * @see Maquina::adicionaB()
         * @return void
         */
        void adicionaB();

        /**
         * mostra_B
         * Mostra o valor de B
         *
         * @return void
         */
        void mostraB();
};



/* ********************************************* */

inline Maquina::Maquina() { }

inline void Maquina::leiaA()
{
    string frase = "Digite o valor de A (número inteiro): ";
    cout<< frase;

    while (!(cin>> this->A)) {
        string buffer;
        cin.clear();
        _clrscr();
        cout<< "Hmmmmm! Você não digitou um número inteiro." << endl;
        cin>> buffer;
        cout<< frase;
    }

    // define B = 0
    this->B = 0;
}


inline bool Maquina::testaA()    { return this->A <= 0; }

inline void Maquina::subtraiA()  { this->A--; }

inline void Maquina::adicionaB() { this->B++; }

inline void Maquina::mostraB()   { cout<< "O valor de B é: "
                                       << this->B << endl; }

Diego_Rocha

E ae cara legal que tu conseguiu XD, só gostaria de falar uma coisa acho que vc usou this, demais tem certas situações em que é necessario o uso do this mais tem certas situações que é exagero por exemplo


class Teste {
   public:
      std::string teste;
      Teste( std::string teste ) {
         this->teste = teste;
     }
     ~Teste(){};
};


Nesse caso seria necessario usar o this, já que teriamos duas variaveis teste declaradas, agora um uso sem necessidade


class Teste {
   public:
      std::string my_teste;
      Teste( std::string teste ) {
         this->my_teste = teste;
     }
     ~Teste(){};
};


aqui o uso é totalmente desnecessario, bastaria declarar my_teste = teste, no mais é isso parabens e bons estudos;

luizvd

Olá Diego... opa obrigado.

É verdade, em situações onde o parâmetro passado é diferente da propriedade da classe, não é necessário o uso do this.

Isso é mais um vício que eu tenho de programar em PHP, onde você só consegue acessar as propriedades/membros da clase com o "$this->", senão não acessa.

Particularmente, apesar de ter que digitar um pouco mais, eu acho mais fácil e rápida a compreensão de quando uma variável é uma propriedade da classe e quando é uma variável temporária e/ou parâmetro :)

Mesmo assim valeu pela dica ;)

Abraços

andrejbo

Porque será que fiquei boqueaberto com a programação aqui descrita???? (diga-se de passagem que, cada vez mais, acho q passar 5anos na universidade é perda de tempo...)

Pensava que o parâmetro this usava-se em casos de apontadores e matrizes e não em casos de variáveis repetidas!!!!
Pessoal, parabéns pelo tópico. Já me ajudou a perceber algumas diferenças entre c e c++ - continuem.