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

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

式の計算電卓コマンド版(2)

shared_ptr<Expr> のラッパークラス ExprRef を作成しました。ChatGPT の提案では Ref<T> でしたが、これはこのままでは使えないので書き直しました。Expr の外部からは ExprRef を使うことができます。これを shared_ptr<Expr> を使わない形に書き直すこともできます。X68000 用としてはその方が良いかもしれないので、今後それもやっていきます。

#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include <memory>
#include <cctype>
#include <vector>
#include <stdexcept>
#include <optional>

using namespace std;

class Expr;
class ExprRef;

using ExprPtr = shared_ptr<Expr>;
using Exprs = vector<ExprRef>;
using Env = map<string, ExprRef>;

// ポインターのラッパークラス
class ExprRef {
    ExprPtr ptr;
public:
    ExprRef() {}
    ExprRef(ExprPtr p) : ptr(std::move(p)) {}
    ExprRef(Expr* raw) : ptr(raw) {}
    ExprRef(double number);
    ExprRef(string name);
    ExprRef(char op, ExprRef expr);
    ExprRef(char op, ExprRef left, ExprRef right);
    operator ExprPtr() { return ptr; }
    double eval(const Env& env) const;
    string to_string(int& pos) const;
    void collect_subexpressions(Exprs& out);
    ExprRef evaluate_once(Env& env);
    ExprRef replace_subexpression(
        int index_to_replace,
        const ExprRef& new_subexpr,
        int& current_index);
    ExprRef replace_subexpression(
        int index_to_replace,
        const ExprRef& new_subexpr);
};

// 式の型定義
class Expr : public enable_shared_from_this<Expr> {
public:
    virtual ~Expr() = default;
    virtual double eval(const Env& env) const = 0;
    virtual string to_string(int& pos) const = 0;
    string pos_string(int& pos) const {
        return "#" + std::to_string(pos++);
    }
    virtual void collect_subexpressions(Exprs& out) = 0;
    virtual ExprRef evaluate_once(Env& env) = 0;
    virtual ExprRef replace_subexpression(
        int index_to_replace,
        const ExprRef& new_subexpr,
        int& current_index) = 0;
};

class Number : public Expr {
    double value;
public:
    Number(double v) : value(v) {}
    double eval(const Env& env) const override { return value; }
    string to_string(int& pos) const override {
        std::ostringstream oss;
        oss << value;
        return oss.str() + pos_string(pos);
    }
    // 番号付きの部分式取得
    void collect_subexpressions(Exprs& out) override {
        out.push_back(shared_from_this());
    }
    // 部分式評価(定数なら簡約、変数なら展開)
    ExprRef evaluate_once(Env& env) override {
        return shared_from_this();
    }
    ExprRef replace_subexpression(
        int index_to_replace,
        const ExprRef& new_subexpr,
        int& current_index) override {
        if (current_index == index_to_replace) {
            current_index++;
            return new_subexpr;
        }
        int this_index = current_index;
        current_index++;
        // Number や Variable は子を持たないのでそのまま返す
        return shared_from_this();
    }
};

class Variable : public Expr {
    string name;
public:
    Variable(string n) : name(std::move(n)) {}
    double eval(const Env& env) const override {
        if (env.count(name)) return env.at(name).eval(env);
        throw runtime_error("Undefined variable: " + name);
    }
    string to_string(int& pos) const override {
        return name + pos_string(pos);
    }
    // 番号付きの部分式取得
    void collect_subexpressions(Exprs& out) override {
        out.push_back(shared_from_this());
    }
    // 部分式評価(定数なら簡約、変数なら展開)
    ExprRef evaluate_once(Env& env) override {
        if (env.count(name)) return env[name];
        return shared_from_this();
    }
    ExprRef replace_subexpression(
        int index_to_replace,
        const ExprRef& new_subexpr,
        int& current_index) override {
        if (current_index == index_to_replace) {
            current_index++;
            return new_subexpr;
        }
        int this_index = current_index;
        current_index++;
        // Number や Variable は子を持たないのでそのまま返す
        return shared_from_this();
    }
};

class UnaryOp : public Expr {
    char op;
    ExprRef expr;
public:
    UnaryOp(char o, ExprRef e) : op(o), expr(e) {}
    double eval(const Env& env) const override {
        double a = expr.eval(env);
        switch (op) {
        case '-': return -a;
        }
        throw runtime_error("Unknown operator");
    }
    string to_string(int& pos) const override {
        string pos_str = pos_string(pos);
        return "(" + string(1, op) + expr.to_string(pos) + ")" + pos_str;
    }
    // 番号付きの部分式取得
    void collect_subexpressions(Exprs& out) override {
        out.push_back(shared_from_this());
        expr.collect_subexpressions(out);
    }
    // 部分式評価(定数なら簡約、変数なら展開)
    ExprRef evaluate_once(Env& env) override {
        auto sub = expr.evaluate_once(env);
        if (auto subnum = dynamic_pointer_cast<Number>(ExprPtr(sub))) {
            double result = UnaryOp(op, ExprRef(subnum)).eval(env);
            return result;
        }
        return shared_from_this();
    }
    ExprRef replace_subexpression(
        int index_to_replace,
        const ExprRef& new_subexpr,
        int& current_index) override {
        if (current_index == index_to_replace) {
            current_index++;
            return new_subexpr;
        }
        int this_index = current_index;
        current_index++;
        auto new_expr = expr.replace_subexpression(index_to_replace, new_subexpr, current_index);
        return ExprRef(op, new_expr);
    }
};

class BinaryOp : public Expr {
    char op;
    ExprRef lhs, rhs;
public:
    BinaryOp(char o, ExprRef l, ExprRef r) : op(o), lhs(l), rhs(r) {}
    double eval(const Env& env) const override {
        double a = lhs.eval(env), b = rhs.eval(env);
        switch (op) {
        case '+': return a + b;
        case '-': return a - b;
        case '*': return a * b;
        case '/': return a / b;
        }
        throw runtime_error("Unknown operator");
    }
    string to_string(int& pos) const override {
        string pos_str = pos_string(pos);
        string left_str = lhs.to_string(pos);
        string right_str = rhs.to_string(pos);
        return "(" + left_str + " " + op + " " + right_str + ")" + pos_str;
    }
    // 番号付きの部分式取得
    void collect_subexpressions(Exprs& out) override {
        out.push_back(shared_from_this());
        lhs.collect_subexpressions(out);
        rhs.collect_subexpressions(out);
    }
    // 部分式評価(定数なら簡約、変数なら展開)
    ExprRef evaluate_once(Env& env) override {
        auto left = lhs.evaluate_once(env);
        auto right = rhs.evaluate_once(env);
        auto lnum = dynamic_pointer_cast<Number>(ExprPtr(left));
        auto rnum = dynamic_pointer_cast<Number>(ExprPtr(right));
        if (lnum && rnum) {
            double result = BinaryOp(op, left, right).eval(env);
            return result;
        }
        return shared_from_this();
    }
    ExprRef replace_subexpression(
        int index_to_replace,
        const ExprRef& new_subexpr,
        int& current_index) override {
        if (current_index == index_to_replace) {
            current_index++;
            return new_subexpr;
        }
        int this_index = current_index;
        current_index++;
        auto new_lhs = lhs.replace_subexpression(index_to_replace, new_subexpr, current_index);
        auto new_rhs = rhs.replace_subexpression(index_to_replace, new_subexpr, current_index);
        return ExprRef(op, new_lhs, new_rhs);
    }
};

// ポインターのラッパークラスの実装
ExprRef::ExprRef(double number) {
    ptr = make_shared<Number>(number);
}
ExprRef::ExprRef(string name) {
    ptr = make_shared<Variable>(name);
}
ExprRef::ExprRef(char op, ExprRef expr) {
    ptr = make_shared<UnaryOp>(op, expr.ptr);
}
ExprRef::ExprRef(char op, ExprRef left, ExprRef right) {
    ptr = make_shared<BinaryOp>(op, left.ptr, right.ptr);
}
double ExprRef::eval(const Env& env) const {
    return ptr->eval(env);
}
string ExprRef::to_string(int& pos) const {
    return ptr->to_string(pos);
}
void ExprRef::collect_subexpressions(Exprs& out) {
    return ptr->collect_subexpressions(out);
}
ExprRef ExprRef::evaluate_once(Env& env) {
    return ptr->evaluate_once(env);
}
ExprRef ExprRef::replace_subexpression(
    int index_to_replace,
    const ExprRef& new_subexpr,
    int& current_index) {
    return ptr->replace_subexpression(index_to_replace, new_subexpr, current_index);
}
ExprRef ExprRef::replace_subexpression(
    int index_to_replace,
    const ExprRef& new_subexpr) {
    int current_index = 0;
    return replace_subexpression(index_to_replace, new_subexpr, current_index);
}

// トークンの型
enum TokenType { NUMBER, IDENT, OP, LPAREN, RPAREN, END, INVALID, HASH, EQUAL, SEMICOLON, KEYWORD };

struct Token {
    TokenType type;
    string text;
};

// 字句解析器
class Tokenizer {
    stringstream ss;
    char curr;
public:
    explicit Tokenizer(const string& input) : ss(input) { next(); }

    void next() { curr = ss.get(); }

    Token getToken() {
        while (isspace(curr)) next();
        if (isdigit(curr)) {
            string num;
            while (isdigit(curr) || curr == '.') {
                num += curr;
                next();
            }
            return { NUMBER, num };
        }
        if (isalpha(curr)) {
            string ident;
            while (isalnum(curr)) {
                ident += curr;
                next();
            }
            if (ident == "var" || ident == "print" || ident == "eval" || ident == "exit")
                return { KEYWORD, ident };
            return { IDENT, ident };
        }
        if (curr == '+' || curr == '-' || curr == '*' || curr == '/') {
            char c = curr;
            next();
            return { OP, string(1, c) };
        }
        if (curr == '(') { next(); return { LPAREN, "(" }; }
        if (curr == ')') { next(); return { RPAREN, ")" }; }
        if (curr == '=') { next(); return { EQUAL, "=" }; }
        if (curr == '#') {
            next();
            string num;
            while (isdigit(curr)) {
                num += curr;
                next();
            }
            return { HASH, num };
        }
        if (curr == ';') { next(); return { SEMICOLON, ";" }; }
        if (curr == EOF) return { END, "" };
        next();
        return { INVALID, string(1, curr) };
    }
};

// 再帰下降構文解析器
class Parser {
    Tokenizer tokenizer;
    Token curr;

    void eat(TokenType type) {
        if (curr.type != type)
            throw runtime_error("Unexpected token: " + curr.text);
        curr = tokenizer.getToken();
    }

    ExprRef parsePrimary() {
        if (curr.type == NUMBER) {
            double val = stod(curr.text);
            eat(NUMBER);
            return val;
        }
        if (curr.type == IDENT) {
            string name = curr.text;
            eat(IDENT);
            return name;
        }
        if (curr.type == OP && curr.text == "-") {
            char op = curr.text[0];
            eat(OP);
            return ExprRef(op, parsePrimary());
        }
        if (curr.type == LPAREN) {
            eat(LPAREN);
            auto expr = parseExpr();
            eat(RPAREN);
            return expr;
        }
        throw runtime_error("Invalid primary expression");
    }

    ExprRef parseTerm() {
        auto left = parsePrimary();
        while (curr.type == OP && (curr.text == "*" || curr.text == "/")) {
            char op = curr.text[0];
            eat(OP);
            auto right = parsePrimary();
            left = ExprRef(op, left, right);
        }
        return left;
    }

    ExprRef parseExpr() {
        auto left = parseTerm();
        while (curr.type == OP && (curr.text == "+" || curr.text == "-")) {
            char op = curr.text[0];
            eat(OP);
            auto right = parseTerm();
            left = ExprRef(op, left, right);
        }
        return left;
    }

public:
    explicit Parser(const string& input) : tokenizer(input) {
        curr = tokenizer.getToken();
    }

    ExprRef parseExpression() {
        return parseExpr();
    }

    optional<string> parseCommand(Env& env) {
        if (curr.type == KEYWORD) {
            string cmd = curr.text;
            eat(KEYWORD);

            if (cmd == "print" || cmd == "eval") {
                string varname = "_";
                int index = 0;
                if (curr.type == IDENT) {
                    varname = curr.text;
                    eat(IDENT);
                }
                if (curr.type == HASH) {
                    index = stoi(curr.text);
                    eat(HASH);
                }
                if (curr.type != END && curr.type != SEMICOLON) {
                    throw runtime_error("Expected command");
                }
                if (!env.count(varname)) throw runtime_error("Undefined variable: " + varname);
                auto root = env[varname];
                Exprs subs;
                root.collect_subexpressions(subs);
                if (index >= 0 && index < subs.size()) {
                    if (cmd == "print") {
                        int pos = index;
                        return subs[index].to_string(pos);
                    }
                    else {
                        auto evaluated = subs[index].evaluate_once(env);
                        env[varname] = env[varname].replace_subexpression(index, evaluated);
                        int pos = 0;
                        return env[varname].to_string(pos);
                    }
                }
                else {
                    throw runtime_error("Invalid subexpression index");
                }
            }

            if (cmd == "var") {
                string varname = "_";
                if (curr.type == IDENT) {
                    varname = curr.text;
                    eat(IDENT);
                }
                if (curr.type == EQUAL) eat(EQUAL);
                auto expr = parseExpression();
                if (curr.type != END && curr.type != SEMICOLON) {
                    throw runtime_error("Expected command");
                }
                env[varname] = expr;
                return "Defined";
            }

            if (cmd == "exit") {
                return nullopt;
            }

            throw runtime_error("Unknown command: " + cmd);
        }

        throw runtime_error("Expected command");
    }
};

// メインループ
int main() {
    Env variables;

    string line;
    cout << "sub-eval-print> ";
    while (getline(cin, line)) {
        try {
            Parser parser(line);
            optional<string> result = parser.parseCommand(variables);
            if (!result) {
                break;
            }
            cout << *result << endl;
        }
        catch (exception& e) {
            cerr << "Error: " << e.what() << endl;
        }
        cout << "sub-eval-print> ";
    }

    return 0;
}