/*
 * =====================================================================================
 * 
 *        Filename:  translator.cpp
 * 
 *     Description:  A translator for brainf*ck
 * 
 *         Version:  1.0
 *         Created:  05/19/2006 05:55:54 PM EDT
 *        Revision:  none
 *        Compiler:  gcc
 * 
 *          Author:   (), 
 *         Company:  
 * 
 * =====================================================================================
 */

#include<iostream>
#include<stack>
#include <vector>
#include <map>
using namespace std;

// Using a linked list to simulate memory
struct mNode {
    mNode *prev;
    mNode *next;
    char name;
    int value;
    bool modified;
} ;

typedef struct mNode memNode;

class Machine {
    private:
        memNode *head;
        memNode *current;
        int size;

    public:
        Machine() {
            size = 0;
            head = newPtr();
            current = head;
        }

        ~Machine() {
            memNode *ptr = head->prev;
            while (ptr != NULL) {
                free(ptr);
                ptr = ptr->prev;
            }
            ptr = head->next;
            while (ptr != NULL) {
                free(ptr);
                ptr = ptr->next;
            }
        }
        void incPtr() {
            if (current->next == NULL) {
                memNode *temp = newPtr();
                temp->prev = current;
                current->next = temp;
            }
            current = current->next;
        }

        void decPtr() {
            if (current->prev == NULL) {
                memNode *temp = newPtr();
                temp->next = current;
                current->prev = temp;
            }
            current = current->prev;
        }

        memNode* newPtr() {
            memNode *temp = (memNode*) malloc(sizeof(memNode));
            temp->prev = NULL;
            temp->next = NULL;
            temp->value = 0;
            temp->name = 'A'+size;
            temp->modified = false;
            size++;
            return temp;
        }

        void incVal(int val) {
            current->value+=val;
        }

        void decVal(int val) {
            current->value-=val;
        }

        char getName() {
            return current->name;
        }

        int getValue() {
            return current->value;
        }

        void setValue(char value) {
            current->value = value;
        }

        void setModified() {
            current->modified = true;
        }

        bool getModified() {
            return current->modified;
        }

        void printOut() {
            memNode *ptr = &*head;
            while (ptr->prev != NULL) 
                ptr = ptr->prev;
            while (ptr != NULL) {
                printf("%c->%i ", ptr->name, ptr->value);
                ptr = ptr->next;
            }

        }
        void printOut(map<char, int> modified) {
            memNode *ptr = &*head;
            while (ptr->prev != NULL) 
                ptr = ptr->prev;
            while (ptr != NULL) {
                if (modified[ptr->name])
                    printf("%c->%i ", ptr->name, ptr->value);
                ptr = ptr->next;
            }

        }

        map<char, int> packMap() {
            map<char, int> ret;
            memNode *ptr = head;
            while (ptr->prev != NULL) {
                ret[ptr->name] = ptr->value;
                ptr = ptr->prev;
            }
            ptr = head;
            while (ptr->next != NULL) {
                ret[ptr->name] = ptr->value;
                ptr = ptr->next;
            }
            return ret;
        }
};

class Interperter {
    private:
        Machine m;
        string line;
        map<int, bool> matchedLoops; // loops that are invariant | loopid->bool
        map<int, bool> doLoops; // loops that 'do' something | loopid->bool
        map<int, int> loopIds; // all the loop IDs | index->id
        map<int, bool> invariant; // keeps track of which loop we already printed out the invariant of.
        stack<int> ip; // pointer stack 
        stack<int> lid; // loop id stack

        // Modifier Tracking variables
        map<int, map<char, int> > modvars; // map from loopid -> map from value to modified
        map<char, int> curmod; // vars being modified in current loop
        stack<map<char, int> > modstack; // stack of which vars are being modified in which loop.

        int count;
        int loopid;
        unsigned int pos; // where we are looking at in the string.

        char *spacing;
        string output;
        int indentlevel;


        void markModded() {
            if (modvars.find(loopid) == modvars.end()) {
                map<char, int> inscope;
                modvars[loopid] = inscope;
            }
            modvars[loopid][m.getName()] = true;
        }

        // Goes through a loop, figuring out:
        // 1. where it starts (places it in loopid)
        // 2. if it does anything other than shift
        // 3. where it ends
        // 4. if the loop is 'matched' (as many < as > inside it)
        int matchLoop(int index) {
            int inc=0, dec=0;
            loopid++;
            int id = loopid;
            doLoops[id] = false;
            loopIds[index] = id;
            for (unsigned int i = index; i < line.size(); i++) {
                if (line[i] == '<') {
                    dec++;
                } else if (line[i] == '>') {
                    inc++;
                } else if (line[i] == ']') {
                    matchedLoops[id] = (inc == dec);
                    for (int j = id+1; j < loopid; j++) {
                        if (!matchedLoops[j]) 
                            matchedLoops[id] = false;
                    }
                    return i;
                } else if (line[i] == '[') {
                    i = matchLoop(++i);
                    if (i < 0)
                        return i;
                } else {
                    doLoops[id] = true;
                }
            }
            return -1;
        }

        int skipLoop() {
            stack<char> p;
            p.push('[');
            while (!p.empty()) {
                pos++;
                if (line[pos] == ']') 
                    p.pop();
                if (line[pos] == '[')
                    p.push('[');
            }
            return pos;
        }

        int countC(char c) {
            count = 0;
            for (; pos < line.size() && line[pos] == c; pos++) {
                count++;
            }
            pos--;
            return count;
        }

        bool isInvariant() {
            return (invariant[loopid] && matchedLoops[loopid]);
        }

        bool toplevel() {
            return (indentlevel == 0);
        }

        bool turn() {
            char c = line[pos];
            char in;
            if (indentlevel > 0) { 
                spacing = (char*) malloc(sizeof(char) * (indentlevel+1));
                memset(spacing, ' ', indentlevel);
                spacing[indentlevel] = '\0';
            } else {
                spacing = "";
            }
            string print = "";
            switch(c) {
                case('>'):
                    m.incPtr();
                    break;
                case('<'):
                    m.decPtr();
                    break;
                case('+'):
                    // mark the variable we modified, so we can display it later.
                    if (!toplevel() /*&& unmodded[loopid]*/) {
                        markModded();
                        curmod[m.getName()] = true;
                    }
                    // count out all the pluses, so we can give a nice value,
                    // instead of saying how much increment was.
                    count = countC('+');
                    m.incVal(count);

                    // if the loop is invariant, we don't print out variable details
                    if (isInvariant()) {
                        printf("%s", spacing);
                        printf("Add %i to %c\n", count, m.getName()); 
                        // otherwise we do
                    } else if (!matchedLoops[loopid] || toplevel()) {
                        printf("%s", spacing);
                        printf("Add %i to %c (%i->%i)\n", count, m.getName(), m.getValue()-count, m.getValue()); 
                    }
                    break;
                case('-'):
                    if (indentlevel > 0 /*&& unmodded[loopid]*/) {
                        markModded();
                        curmod[m.getName()] = true;
                    }
                    count = countC('-');
                    m.decVal(count);

                    if (isInvariant()) {
                        printf("%s", spacing);
                        printf("Subtract %i from %c\n", count, m.getName()); 
                    } else if (!matchedLoops[loopid] || toplevel()) {
                        printf("%s", spacing);
                        printf("Subtract %i from %c (%i->%i)\n", count, m.getName(), m.getValue()+count, m.getValue()); 
                    }
                    break;
                case('.'):
                    if (isInvariant()) {
                        printf("%s", spacing);
                        printf("Print %c\n", m.getName());
                    } else if (!matchedLoops[loopid] || toplevel()) {
                        printf("%s", spacing);
                        printf("Print %c (%i)\n", m.getName(), m.getValue());
                    }
                    output += m.getValue();
                    break;
                case(','):
                    cin >> in;
                    if (isInvariant()) {
                        printf("Read %i into %c (%i->%i)\n", in, m.getName(), m.getValue(), in);
                    } else if (!matchedLoops[loopid] || toplevel()) {
                        printf("%s", spacing);
                        printf("Read into %c\n", m.getName());
                    }
                    m.setValue(in);
                    m.setModified();
                    break;
                case('['):
                    loopid = loopIds[pos+1];
                    // need to skip to the closing bracket if the loop invariant doesn't hold.
                    if (m.getValue() <= 0) {
                        printf("%s", spacing);
                        printf("Skipping loop %i (%c->%i)\n", loopid, m.getName(), m.getValue());
                        pos = skipLoop();
                        break;
                    }
                    // raise the indent level, since we are in a loop.
                    indentlevel++;
                    // if an action is done in this loop (print, read, inc val, dec val)
                    // let's show it, otherwise we don't care (when moving the read head, f.e.)
                    if (doLoops[loopid]) {
                        printf("%s", spacing);
                        printf("Entering loop %i (%i): ", loopid, pos);
                        lid.push(loopid);

                        // create a map for what is modified during this loop
                        // we'll print it back out when we exit the loop.
                        curmod = map<char, int>();
                        modstack.push(curmod);

                        // if the loop is invariant, then we describe the conditional
                        // otherwise, we unroll the loop to be examined.
                        if (matchedLoops[loopid]) {
                            invariant[loopid] = true;
                            printf("(Loop invariant: %c->%i)\n", m.getName(), m.getValue());
                        } else {
                            printf("(Loop is unrolled)\n");
                        }
                    }
                    ip.push(pos);
                    break;
                case(']'):
                    if (m.getValue() != 0) {
                        if (matchedLoops[loopid]) {
                            invariant[loopid] = false;
                        }
                        pos = ip.top();
                    } else {
                        ip.pop();
                        indentlevel--;
                        // if loopid is an invariant loop, let's print the exit values.
                        // and if the loop actually does something.
                        if (doLoops[lid.top()]) {
                            if (matchedLoops[lid.top()]) {
                                spacing[indentlevel] = '\0';
                                printf("%s", spacing);
                                printf("Exiting loop %i: ", lid.top());
                                m.printOut(curmod);
                                printf("\n");
                                printf("\n");
                                lid.pop();
                                modstack.pop();
                            }
                        }
                    }
                    break;
                default:
                    break;
            }
            pos++;

            return pos < line.size();
        }



    public:
        Interperter(string l) {
            line = l;
            loopid = 0;
            pos = 0;
            indentlevel = 0;
            for (int i = 0; i < (signed int)line.size(); i++) {
                if (line[i] == ']') {
                    printf("Error: mismatched braces\n");
                    exit(0);
                }
                if (line[i] == '[') {
                    //            printf("Trying to match %i\n", i);
                    i = matchLoop(++i);
                    if (i < 0) {
                        printf("Error: mismatched braces\n");
                        exit(0);
                    }
                }
            }
        }

        ~Interperter() {
        }

        void run() {
            while (turn());
        }

        void source() {
            printf("***Source***\n");
            printf("%s\n", line.c_str());
        }

        void step() {
            turn();
        }

        void summary() {
            printf("\n***Summary***\n");
            printf("Output:\n%s\n\n", output.c_str());

            for (int i = 1; i <= loopid; i++) {
                printf("Variables modified in loop %i: \n", i);
                for (map<char,int>::iterator it = modvars[i].begin(); it != modvars[i].end(); it++) {
                    printf("%c ", it->first);
                }
                printf("\n");
            }
            printf("\n");
            printf("Contents of memory:\n");
            m.printOut();
            printf("\n");
        }
    
        map<char, int> getMemory() {
            return m.packMap();
        }

        string getSource() {
            return line;
        }

        int getPos() {
            return pos;
        }
};

int main(int argc, char **argv) {
    string s;
    string line = "";
    while (cin >> s) {
        line += s;
        line += '\n';
    }

    Interperter interp(line);
    interp.source();
    interp.run();
    interp.summary();

    return 0;
}

