RCSNUSP/C++

From Rosetta Code

This is a simple C++ implementation of Core and Modular SNUSP, Bloated will (maybe) come in the future.

To execute the SNUSP program just start the interpreter with its file name as parameter, like so: interpreter name SNUSP program.

It has a simple Log feature, to use it type: interpreter name /L SNUSP program.

Input and output are thru Console.

C++


#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <iomanip>

#define msg0 "|             *** end of code ***              |"
#define msg1 "|            *** stack is empty ***            |"
#define msg2 "|               *?* step out *?*               |"
#define msg3 "|               *!* step out *!*               |"
#define msg4 "|            *** memory blows up ***           |"

typedef unsigned char u8;

enum direction {
    UP, DOWN, LEFT, RIGHT
};

class stackFrame {
public:
    stackFrame() { }
    stackFrame( int ipc, int ipl, direction dir) :
    IPC( ipc ), IPL( ipl ), DIR( dir ) { }
    int IPC, IPL;
    direction DIR;
} ;

class SNUSP {
public:
    SNUSP() : IPC( 0 ), IPL( 0 ), MPT( 0 ), DIR( RIGHT ) { 
        memory.push_back( 0 ); 
    }

    ~SNUSP() { 
        memory.clear();
        code.clear();
    }

    void run( std::string filename, bool lg ) {
        logging = lg;
        if( logging ) {
            std::ofstream o( "log.txt", std::ios_base::trunc );
            strm << "+------+-------+-------+-------+-------+-------+\n| INST |  IPC  |  IPL  |  MPT  |  DIR  |  MEM  |";
            log( strm.str() );
        }

        if( !openFile( filename ) ) {
            std::cout << "Cannot open file '" << filename << "'\n";
            return;
        }

        while( execute( code[IPL][IPC] ) );
        std::cout << "\n\n" << memory[MPT];
    }
private:
    bool execute( u8 u ) {
        stackFrame sf;
        switch( u ) {
            case '<':
                if( --MPT < 0 ) {
                    if( logging ) log( msg4 );
                    return false;
                }
            break;
            case '>':
                if( ++MPT >= static_cast<int>( memory.size() ) ) { memory.push_back( 0 ); }
            break;
            case '+':
                memory[MPT]++;
            break;
            case '-':
                memory[MPT]--;
            break;
            case ',':
                std::cin >> memory[MPT];
            break;
            case '.':
                std::cout << static_cast<u8>( memory[MPT] );
            break;
            case '!':
                if( step() ) {
                    if( logging ) log( msg3 );
                    return false;
                }
            break;
            case '?':
                if( !memory[MPT] ) {
                    if( step() ) {
                        if( logging ) log( msg2 );
                        return false;
                    }
                }
            break;
            case '@':
                stack.push_back( stackFrame( IPC + ( DIR == RIGHT ? 1 : DIR == LEFT ? -1 : 0 ), 
                                             IPL + ( DIR == DOWN  ? 1 : DIR == UP   ? -1 : 0 ), DIR ) );
            break;
            case '#':
                if( !stack.size() ) {
                    if( logging ) log( msg1);
                    return false;
                }
                sf = stack.back();
                stack.pop_back();
                IPC = sf.IPC; IPL = sf.IPL; DIR = sf.DIR;
            break;
            case '/':
                DIR = DIR == RIGHT ? UP : DIR == LEFT ? DOWN : DIR == DOWN ? LEFT : RIGHT;
            break;
            case '\\':
                DIR = DIR == RIGHT ? DOWN : DIR == LEFT ? UP : DIR == DOWN ? RIGHT : LEFT;
            break;
        }

        if( logging ) {
            strm << "|" << std::setw( 5 ) << u  << " |" << std::setw( 6 ) << IPC << " |" << std::setw( 6 ) 
            << IPL << " |" << std::setw( 6 ) << MPT << " |" << std::setw( 6 ) << DIR << " |" << std::setw( 6 )           << memory[MPT] << " |";
            log( strm.str() );
        }

        if( step() ) {
            if( logging ) log( msg0 );
            return false;
        }

        return true;
    }

    bool step() {
        IPC += DIR == RIGHT ? 1 : DIR == LEFT ? -1 : 0;
        IPL += DIR == DOWN  ? 1 : DIR == UP   ? -1 : 0;
        return ( IPL >= static_cast<int>( code.size() ) || IPL < 0 || 
                 IPC >= static_cast<int>( code[IPL].length() ) || IPC < 0 );
    }

    bool openFile( std::string filename ) {
        std::ifstream in;
        in.open( filename.c_str() );
        if( !in.good() ) {
            return false;
        }

        std::string line;
        size_t max_len = 0, len;
        while( std::getline( in, line ) ) {
            len = line.length();
            if( max_len < len ) max_len = len;
            code.push_back( line );
            if( !IPC && !IPL ) {
                std::size_t i = static_cast<int>( line.find( '$' ) );
                if( i < line.npos ) {
                    IPL = code.size() - 1;
                    IPC = i;
                }
            }
        }
        in.close();

        for( std::vector<std::string>::iterator i = code.begin(); i != code.end(); i++ ) {
            if( ( *i ).length() < max_len ) ( *i ).insert( ( *i ).end(), max_len - ( *i ).length(), ' ' );
        }
        return true;
    }

    void log( std::string msg )
    {
        std::ofstream logDegug( "log.txt", std::ios_base::out | std::ios_base::app );
        logDegug << msg << "\n+------+-------+-------+-------+-------+-------+" << std::endl;
        strm.str( "" );
    }

    std::vector<std::string>            code;
    std::vector<stackFrame>             stack;
    std::vector<int>                    memory;
    std::stringstream                   strm;
    int                                 IPC, IPL, MPT;
    direction                           DIR;
    bool                                logging;
};

int main( int argc,  char* argv[] ) {
    if( argc < 2 ) {
        std::cout << "\nDoh!!!\n\nUsage:\t[/L] Filename\n\n\t/L\tLog each command to a log file";
    } else {
        SNUSP s;
        s.run( argv[argc == 3 ? 2 : 1], ( argv[1][0] == '/' && toupper( argv[1][1] ) == 'L' ) );
    }
    std::cout << "\n\n";
    return 0;
}