ChatGPT で全部作ってもらうのは難しいので、いったん以下のようなプログラムを ChatGPT で作ってもらって、動くように修正しました。
構文
構文を以下のどれかとします。
- var 変数名 = 式
- print 変数名 # 番号
- eval 変数名 # 番号
ここで
動作
構文解析の結果に以下の処理を行います。
var 変数名 = 式
変数を定義します。変数名が省略されたときは「_」という名前の変数を定義します。
print 変数名 # 番号
変数に定義された式を表示します。変数名が省略されたときは「_」という名前の変数に定義された式を表示します。式の各「部分式」には番号が付けられていて、番号が指定されたときはその番号の部分式を表示します。番号を省略すると式全体を表示します。
eval 変数名 # 番号
変数に定義された式を計算します。変数名が省略されたときは「_」という名前の変数に定義された式を計算します。式の各「部分式」には番号が付けられていて、番号が指定されたときはその番号の部分式を計算して、その部分式を計算結果で置き換えます。番号を省略すると式全体を計算します。
「計算する」とは
- 式が二項演算または単項演算で各演算数が数値のとき、その式を演算の結果で置き換えます。
- 式が変数のとき、その式を変数に定義された式で置き換えます。
プログラムのコード
動くように修正したものは以下のようになります。
#include <iostream> #include <sstream> #include <string> #include <map> #include <memory> #include <cctype> #include <vector> #include <stdexcept> #include <optional> using namespace std; // 式の型定義 struct Expr { virtual ~Expr() = default; virtual double eval(const map<string, shared_ptr<Expr>>& env) const = 0; virtual string to_string(int& pos) const = 0; string pos_string(int& pos) const { return "#" + std::to_string(pos++); } }; struct Number : Expr { double value; Number(double v) : value(v) {} double eval(const map<string, shared_ptr<Expr>>& env) const override { return value; } string to_string(int& pos) const override { return std::to_string(value) + pos_string(pos); } }; struct Variable : Expr { string name; Variable(string n) : name(std::move(n)) {} double eval(const map<string, shared_ptr<Expr>>& 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); } }; struct UnaryMinus : Expr { shared_ptr<Expr> expr; UnaryMinus(shared_ptr<Expr> e) : expr(std::move(e)) {} double eval(const map<string, shared_ptr<Expr>>& env) const override { return -expr->eval(env); } string to_string(int& pos) const override { string pos_str = pos_string(pos); return "-(" + expr->to_string(pos) + ")" + pos_str; } }; struct BinaryOp : Expr { char op; shared_ptr<Expr> lhs, rhs; BinaryOp(char o, shared_ptr<Expr> l, shared_ptr<Expr> r) : op(o), lhs(std::move(l)), rhs(std::move(r)) {} double eval(const map<string, shared_ptr<Expr>>& 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; } }; // トークンの型 enum TokenType { NUMBER, IDENT, OP, LPAREN, RPAREN, END, INVALID, HASH, EQUAL, 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 == 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(); } shared_ptr<Expr> parsePrimary() { if (curr.type == NUMBER) { double val = stod(curr.text); eat(NUMBER); return make_shared<Number>(val); } if (curr.type == IDENT) { string name = curr.text; eat(IDENT); return make_shared<Variable>(name); } if (curr.type == OP && curr.text == "-") { eat(OP); return make_shared<UnaryMinus>(parsePrimary()); } if (curr.type == LPAREN) { eat(LPAREN); auto expr = parseExpr(); eat(RPAREN); return expr; } throw runtime_error("Invalid primary expression"); } shared_ptr<Expr> parseTerm() { auto left = parsePrimary(); while (curr.type == OP && (curr.text == "*" || curr.text == "/")) { char op = curr.text[0]; eat(OP); auto right = parsePrimary(); left = make_shared<BinaryOp>(op, left, right); } return left; } shared_ptr<Expr> parseExpr() { auto left = parseTerm(); while (curr.type == OP && (curr.text == "+" || curr.text == "-")) { char op = curr.text[0]; eat(OP); auto right = parseTerm(); left = make_shared<BinaryOp>(op, left, right); } return left; } public: explicit Parser(const string& input) : tokenizer(input) { curr = tokenizer.getToken(); } shared_ptr<Expr> parseExpression() { return parseExpr(); } // 番号付きの部分式取得 void collect_subexpressions(const shared_ptr<Expr>& expr, vector<shared_ptr<Expr>>& out) { if (!expr) return; out.push_back(expr); if (auto bin = dynamic_pointer_cast<BinaryOp>(expr)) { collect_subexpressions(bin->lhs, out); collect_subexpressions(bin->rhs, out); } else if (auto un = dynamic_pointer_cast<UnaryMinus>(expr)) { collect_subexpressions(un->expr, out); } } // 部分式評価(定数なら簡約、変数なら展開) shared_ptr<Expr> evaluate_once(shared_ptr<Expr> expr, map<string, shared_ptr<Expr>>& env) { if (auto v = dynamic_pointer_cast<Variable>(expr)) { if (env.count(v->name)) return env[v->name]; } else if (auto bin = dynamic_pointer_cast<BinaryOp>(expr)) { auto lhs = bin->lhs, rhs = bin->rhs; auto left = evaluate_once(lhs, env); auto right = evaluate_once(rhs, env); auto lnum = dynamic_pointer_cast<Number>(left); auto rnum = dynamic_pointer_cast<Number>(right); if (lnum && rnum) { double result = BinaryOp(bin->op, left, right).eval(env); return make_shared<Number>(result); } } else if (auto un = dynamic_pointer_cast<UnaryMinus>(expr)) { auto sub = evaluate_once(un->expr, env); if (auto subnum = dynamic_pointer_cast<Number>(sub)) { return make_shared<Number>(-subnum->value); } } return expr; } shared_ptr<Expr> replace_subexpression(const shared_ptr<Expr>& expr, int index_to_replace, const shared_ptr<Expr>& new_subexpr, int& current_index) { if (current_index == index_to_replace) { current_index++; return new_subexpr; } int this_index = current_index; current_index++; if (auto bin = dynamic_pointer_cast<BinaryOp>(expr)) { auto new_lhs = replace_subexpression(bin->lhs, index_to_replace, new_subexpr, current_index); auto new_rhs = replace_subexpression(bin->rhs, index_to_replace, new_subexpr, current_index); return make_shared<BinaryOp>(bin->op, new_lhs, new_rhs); } if (auto un = dynamic_pointer_cast<UnaryMinus>(expr)) { auto new_expr = replace_subexpression(un->expr, index_to_replace, new_subexpr, current_index); return make_shared<UnaryMinus>(new_expr); } // Number や Variable は子を持たないのでそのまま返す return expr; } // 外から使いやすいようにラップする関数 shared_ptr<Expr> replace_subexpression(const shared_ptr<Expr>& expr, int index_to_replace, const shared_ptr<Expr>& new_subexpr) { int current_index = 0; return replace_subexpression(expr, index_to_replace, new_subexpr, current_index); } optional<string> parseCommand(map<string, shared_ptr<Expr>>& 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 (!env.count(varname)) throw runtime_error("Undefined variable: " + varname); auto root = env[varname]; vector<shared_ptr<Expr>> subs; collect_subexpressions(root, subs); if (index >= 0 && index < subs.size()) { if (cmd == "print") { int pos = index; return subs[index]->to_string(pos); } else { auto evaluated = evaluate_once(subs[index], env); env[varname] = replace_subexpression(env[varname], 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(); env[varname] = expr; return "Defined"; } if (cmd == "exit") { return nullopt; } throw runtime_error("Unknown command: " + cmd); } throw runtime_error("Expected command"); } }; // メインループ int main() { map<string, shared_ptr<Expr>> 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; }

