view interps/pbrain/pbrain.cc @ 9554:23f43464694e

<Zarutian> le/rn Frams\xc3\xb3knarflokkurinn/A, now defunct, political party in Iceland. Like its sister party Sj\xc3\xa1lfst\xc3\xa6\xc3\xb0isflokkurinn it is named by the antonym of what it is. (The name means the Progressive Party but they have nearly always been highly regressive). Think dumb Hill-Billies in ill fitting suits and you get their constiuents.
author HackBot
date Sun, 30 Oct 2016 14:33:24 +0000
parents 859f9b4339e6
children
line wrap: on
line source

/*
Interpreter for the pbrain programming language (procedural Brainf**k)
Copyright(C) Paul M. Parks
All Rights Reserved.

v1.4.3
2004/07/15 12:10

paul@parkscomputing.com
http://www.parkscomputing.com/

The syntax is the same as traditional Brainf**k, with the following 
symbols added:

(
Begin procedure

)
End procecure

:
Call procedure identified by the value at the current location

   
Procedures are identified by numeric ID:

+([-])

Assuming the current location is zero, defines a procedure number 1 that 
sets the current location to zero when called.

++(<<[>>+<<-]>[>+<-]>)

Assuming the current location is zero, defines a procedure number 2 that 
accepts two parameters. It adds parameter 1 and parameter 2 and places 
the result in the location that was current when the procedure was 
called, zeroing out parameters 1 and 2 in the process.

+++([-]>++++++++++[<++++>-]<++++++++>[-]++:.)

Assuming the current location is zero, defines a procedure 3 that prints 
the ASCII equivalent of the numeral at the current location, between 0 
and 9.

+++>+++++>++:

Calls procedure 2, passing in parameters 3 and 5.

All of the above examples may be combined into the program below. Note that 
the procedures are numbered 1, 2, and 3 because the current location is 
incremented prior to each procedure definition.

+([-])
+(-:<<[>>+<<-]>[>+<-]>)
+([-]>++++++++++[<++++>-]<++++++++>[-]++:.)
>+++>+++++>++:
>+++:

An error condition is reported with a short diagnostic to stderr and an 
error number returned from the executable. Errors reported by the 
interpreter are as follows:

1 - Out of memory
2 - Unknown procedure
3 - Memory address out of range
4 - Cannot find matching ] for beginning [
999 - Unknown exception

*/


#include <vector>
#include <iostream>
#include <fstream>
#include <iterator>
#include <map>
#include <cstdlib>


#if defined(_MSC_VER)
#pragma warning(disable: 4571)
#endif

// Define the type contained in the memory array
#ifndef PBRAIN_MEM_TYPE
#define PBRAIN_MEM_TYPE int
#endif

// Define the character input/output type.
#ifndef PBRAIN_CHARACTER_TYPE
#define PBRAIN_CHARACTER_TYPE wchar_t
#endif

// Set the initial size of the memory array, if not defined externally.
#ifndef PBRAIN_INIT_MEM_SIZE
#define PBRAIN_INIT_MEM_SIZE 30000
#endif

// By default, use a dynamic array to store memory locations.
#ifndef PBRAIN_STATIC_MEMORY
typedef std::vector<PBRAIN_MEM_TYPE> Mem;
Mem mem(PBRAIN_INIT_MEM_SIZE);
Mem::size_type mp = 0;
#else
PBRAIN_MEM_TYPE mem[PBRAIN_INIT_MEM_SIZE];
size_t mp = 0;
#endif


// Placeholder template class to be specialized below.
template<typename Ch> struct io_types{};


// Define appropriate I/O and stream iterator types for working with byte 
// characters.
template<> struct io_types<char>
{
    static std::istream& cin;
    static std::ostream& cout;
    typedef std::basic_ifstream<char, std::char_traits<char> > ifstream;
    typedef std::istream_iterator<char,char> istream_iterator;
};

std::istream& io_types<char>::cin = std::cin;
std::ostream& io_types<char>::cout = std::cout;


// Define appropriate I/O and stream iterator types for working with wide 
// characters.
template<> struct io_types<wchar_t>
{
    static std::wistream& cin;
    static std::wostream& cout;
    typedef std::basic_ifstream<wchar_t, std::char_traits<wchar_t> > ifstream;
    typedef std::istream_iterator<wchar_t,wchar_t> istream_iterator;
};

std::wistream& io_types<wchar_t>::cin = std::wcin;
std::wostream& io_types<wchar_t>::cout = std::wcout;


// Useful type that chooses the appropriate typedefs for the character width
typedef io_types<PBRAIN_CHARACTER_TYPE> io;

// Type for storing a string of instructions; used for procedures and loops
typedef std::vector<PBRAIN_CHARACTER_TYPE> SourceBlock;

// Type for storing procedures indexed by number
typedef std::map<PBRAIN_MEM_TYPE, std::vector<PBRAIN_CHARACTER_TYPE> > Procedures;


// Map of procedure IDs to procedures
Procedures procedures;


// Interpret a container of instructions
template<typename It> void interpret(It ii, It eos)
{
    while (ii != eos)
    {
        switch (*ii)
        {
            case '+':
                ++mem[mp];         
                break;

            case '-':
                --mem[mp];
                break;

            case '>':
                ++mp;

#ifndef PBRAIN_STATIC_MEMORY
                // If memory is kept in a dynamic array, the array will grow as 
                // needed.
                try
                {
                    if (mp == mem.size())
                    {
                        mem.resize(mem.size() * 2);
                    }
                }
                catch (...)
                {
                    // Ostensibly an out-of-memory condition.
                    throw 1;
                }
#else
                // Static memory cannot grow, so throw when limit reached
                if (mp == sizeof(mem) / sizeof(PBRAIN_MEM_TYPE))
                {
                    throw 1;
                }
#endif

                break;

            case '<':
                --mp;

                // Throw out-of-range error if cell location is decremented below 0
                if (static_cast<int>(mp) < 0)
                {
                    throw 3;
                }

                break;

            case '.':
                io::cout.put(static_cast<PBRAIN_CHARACTER_TYPE>(mem[mp]));
                break;

            case ',':
                mem[mp] = static_cast<PBRAIN_MEM_TYPE>(io::cin.get());
                break;

            case '[':
                // Move to first instruction in the loop
                ++ii;

                {
                    int nest = 0;
                    It begin = ii;

                    // Find the matching ]
                    while (ii != eos)
                    {
                        if (*ii == '[')
                        {
                            ++nest;
                        }
                        else if (*ii == ']')
                        {
                            if (nest)
                            {
                                --nest;
                            }
                            else
                            {
                                break;
                            }
                        }

                        ++ii;
                    }

                    // If no matching ] is found in source block, report error.
                    if (ii == eos)
                    {
                        throw 4;
                    }

                    // At this point the iterator will point at the matching ] 
                    // character, which is one instruction past the end of the range 
                    // of instructions to be processed in a loop.
                    loop(begin, ii);
                }

                break;

            case '(':
                ++ii;

                {
                    SourceBlock sourceBlock;

                    while (ii != eos && *ii != ')')
                    {
                        sourceBlock.push_back(*ii);
                        ++ii;
                    }

                    procedures.insert(std::make_pair(mem[mp], sourceBlock));
                }

                break;

            case ':':
            {
                // Look up the source block that matches the value at the current 
                // location. If found, execute it.
                Procedures::iterator i = procedures.find(mem[mp]);

                if (i != procedures.end())
                {
                    interpret(i->second.begin(), i->second.end());
                }
                else
                {
                    throw 2;
                }
            }
            break;

            default:
                break;
        }

        ++ii;
    }
}


template<typename It> void loop(It ii, It eos)
{
    // Interpret instructions until the value in the current memory location 
    // is zero
    while (mem[mp])
    {
        interpret(ii, eos);
    }
}


template<typename C> void parse(C& c)
{
    io::istream_iterator ii(c);
    io::istream_iterator eos;

    SourceBlock sourceBlock;

    // Copy instructions from the input stream to a source block.
    while (ii != eos)
    {
        sourceBlock.push_back(*ii);
        ++ii;
    }

    // Execute the instructions in the source block
    interpret(sourceBlock.begin(), sourceBlock.end());
}


int main(int argc, char** argv)
{
    try
    {
        // Read from a file if a filename is provided as an argument.
        if (argc > 1)
        {
            io::ifstream source(argv[1]);

            if (source.is_open())
            {
                parse(source);
            }
        }
        // Otherwise interpret code from stdin
        else
        {
            parse(io::cin);
        }
    }
    catch(int e)
    {
        std::cerr << "Error " << e << ", cell " << (unsigned int)(mp) << "\n";
        exit(e);
    }
    catch(...)
    {
        std::cerr << "Error " << 999 << ", cell " << (unsigned int)(mp) << "\n";
        exit(999);
    }
}