非専門的シンギュラリティー研究所

無限に動き続けるシステムを表す方法を AI なども使って考えていきます。

昔ながらの BASIC(2)

C++ の調査(10) - 非専門的シンギュラリティー研究所」のコンパイルの処理で行番号を出力するようにします。

void compileExpression(const string& expr, int& line_number) {
    vector<string> postfix = toPostfix(expr);
    bool first = true;
    for (const string& tok : postfix) {
        if (!first) {
            cout << ++line_number << " ";
        }
        first = false;
        if (tok == "+" || tok == "-" || tok == "*" || tok == "/") {
            if (tok == "+") cout << "ADD\n";
            if (tok == "-") cout << "SUB\n";
            if (tok == "*") cout << "MUL\n";
            if (tok == "/") cout << "DIV\n";
        }
        else if (tok == "u-") {
            cout << "NEG\n";
        }
        else {
            if (isdigit(tok[0]) || (tok[0] == '-' && tok.size() > 1))
                cout << "PUSH " << tok << "\n";
            else
                cout << "LOAD " << tok << "\n";
        }
    }
}

void compileProgram() {
    cout << "=== COMPILED OUTPUT ===\n";
    for (auto& [ln, code] : program) {
        stringstream ss(code);
        string cmd;
        ss >> cmd;
        cout << ln << " ";
        if (cmd == "LET") {
            string var, eq;
            ss >> var >> eq;
            string expr;
            getline(ss, expr);
            int line_number = ln;
            compileExpression(expr, line_number);
            cout << ++line_number << " ";
            cout << "STORE " << var << "\n";
        }
        else if (cmd == "PRINT") {
            string expr;
            getline(ss, expr);
            int line_number = ln;
            compileExpression(expr, line_number);
            cout << ++line_number << " ";
            cout << "OUT\n";
        }
        else if (cmd == "INPUT") {
            string var;
            ss >> var;
            cout << "IN " << var << "\n";
        }
        else if (cmd == "IF") {
            string cond;
            getline(ss, cond);
            size_t thenPos = cond.find("THEN");
            if (thenPos != string::npos) {
                string condition = cond.substr(0, thenPos);
                string action = cond.substr(thenPos + 4);
                string op;
                string cmpOps[] = { "<=", ">=", "<>", "=", "<", ">" };
                for (const string& co : cmpOps) {
                    size_t pos = condition.find(co);
                    if (pos != string::npos) {
                        string lhs = condition.substr(0, pos);
                        string rhs = condition.substr(pos + co.size());
                        int line_number = ln;
                        compileExpression(lhs, line_number);
                        cout << ++line_number << " ";
                        compileExpression(rhs, line_number);
                        if (co == "=") op = "EQ";
                        if (co == "<") op = "LT";
                        if (co == ">") op = "GT";
                        if (co == "<=") op = "LE";
                        if (co == ">=") op = "GE";
                        if (co == "<>") op = "NE";
                        cout << ++line_number << " ";
                        cout << op << "\n";
                        cout << ++line_number << " ";
                        cout << "JNZ " << action << "\n";
                        break;
                    }
                }
            }
        }
        else if (cmd == "GOTO") {
            int target;
            ss >> target;
            cout << "JMP " << target << "\n";
        }
        else if (cmd == "GOSUB") {
            int target;
            ss >> target;
            cout << "CALL " << target << "\n";
        }
        else if (cmd == "RETURN") {
            cout << "RET\n";
        }
        else if (cmd == "END") {
            cout << "HALT\n";
        }
        else if (cmd == "REM") {
            string cmt;
            getline(ss, cmt);
            cout << "REM" << cmt << "\n";
        }
        else {
            cout << "REM\n";
        }
    }
    cout << "=== END COMPILE ===\n";
}

C++ の調査(10) - 非専門的シンギュラリティー研究所」のフィボナッチ数列のサンプルを仮想命令にコンパイルします。

10 REM フィボナッチ数列
20 PUSH 10
21 STORE N
30 PUSH 1
31 STORE I
40 PUSH 1
41 STORE A
50 PUSH 1
51 STORE B
60 LOAD A
61 OUT
70 LOAD B
71 STORE T
80 LOAD A
81 LOAD B
82 ADD
83 STORE B
90 LOAD T
91 STORE A
100 LOAD I
101 PUSH 1
102 ADD
103 STORE I
110 LOAD I
111 LOAD N
112 LE
113 JNZ  60

C++ の調査(10) - 非専門的シンギュラリティー研究所」のコードに仮想命令のインタープリターを追加します。

stack<int> dataStack;

void unary_op(function<int(int)> op)
{
    int x = dataStack.top();
    dataStack.pop();
    dataStack.push(op(x));
}

void binary_op(function<int(int, int)> op)
{
    int y = dataStack.top();
    dataStack.pop();
    int x = dataStack.top();
    dataStack.pop();
    dataStack.push(op(x, y));
}

void binary_comp_op(function<bool(int, int)> op)
{
    binary_op([op](int x, int y) -> int {return op(x, y) ? -1 : 0; });
}

// アセンブリ言語の行を実行
bool executeAsmLine(const string& line, map<int, string>::iterator& it) {
    stringstream ss(line);
    string cmd;
    ss >> cmd;

    cmd = to_upper(cmd);

    if (cmd == "PUSH") {
        int num;
        ss >> num;
        dataStack.push(num);
    }
    else if (cmd == "LOAD") {
        string var;
        ss >> var;
        dataStack.push(variables[var]);
    }
    else if (cmd == "STORE") {
        string var;
        ss >> var;
        variables[var] = dataStack.top();
        dataStack.pop();
    }
    else if (cmd == "OUT") {
        cout << dataStack.top() << endl;
        dataStack.pop();
    }
    else if (cmd == "IN") {
        string var;
        ss >> var;
        cout << "? ";
        cin >> variables[var];
    }
    else if (cmd == "ADD") {
        binary_op([](int x, int y) -> int {return x + y; });
    }
    else if (cmd == "SUB") {
        binary_op([](int x, int y) -> int {return x - y; });
    }
    else if (cmd == "MUL") {
        binary_op([](int x, int y) -> int {return x * y; });
    }
    else if (cmd == "DIV") {
        binary_op([](int x, int y) -> int {return x / y; });
    }
    else if (cmd == "NEG") {
        unary_op([](int x) -> int {return -x; });
    }
    else if (cmd == "EQ") {
        binary_comp_op([](int x, int y) -> int {return x == y; });
    }
    else if (cmd == "NE") {
        binary_comp_op([](int x, int y) -> int {return x != y; });
    }
    else if (cmd == "LT") {
        binary_comp_op([](int x, int y) -> int {return x < y; });
    }
    else if (cmd == "LE") {
        binary_comp_op([](int x, int y) -> int {return x <= y; });
    }
    else if (cmd == "GT") {
        binary_comp_op([](int x, int y) -> int {return x > y; });
    }
    else if (cmd == "GE") {
        binary_comp_op([](int x, int y) -> int {return x >= y; });
    }
    else if (cmd == "JMP") {
        int target;
        ss >> target;
        it = program.find(target);
        return true;
    }
    else if (cmd == "JNZ") {
        int target;
        ss >> target;
        int cond = dataStack.top();
        dataStack.pop();
        if (cond) {
            it = program.find(target);
            return true;
        }
    }
    else if (cmd == "CALL") {
        int target;
        ss >> target;
        returnStack.push(next(it));
        it = program.find(target);
        return true;
    }
    else if (cmd == "RET") {
        it = returnStack.top();
        returnStack.pop();
        return true;
    }
    else if (cmd == "HALT") {
        // プログラムを中断
        if (it != program.end()) {
            ++it;
        }
        return false;
    }
    else if (cmd == "LIST") {
        // プログラムを表示
        for (auto& [ln, code] : program) {
            cout << ln << " " << code << endl;
        }
    }
    else if (cmd == "RUN") {
        // プログラムを実行
        it = program.begin();
        while (it != program.end()) {
            if (!executeAsmLine(it->second, it)) {
                return true;
            }
        }
        return true;
    }
    else if (cmd == "EXIT") {
        // アセンブリ言語 インタープリターを終了
        exit(0);
    }
    else if (cmd == "RENUM") {
        // 行番号を揃える
        program = renum(renumAsmLine);
        // プログラムが変更されたときは実行位置をリセット
        it = program.begin();
        return true;
        }
    else if (cmd == "REM") {
        // コメント
    }
    else if (cmd == "BASIC") {
        lang = "basic";
    }
    else if (cmd == "ASM") {
        lang = "asm";
    }
    else {
        cout << "コマンドが誤っています:" << cmd;
    }
    if (it != program.end()) {
        ++it;
        return true;
    }
    return false;
}

フィボナッチ数列のサンプルを実行すると以下のようになります。

1
1
2
3
5
8
13
21
34
55