C++でSchemeインタプリタを作ろう
少し作った。
(define (factorial n) (if (= n 1) 1 (* n (factorial (- n 1))))) ; ok (factorial 10) ; 3628800
おー。Accelerated C++ と Effective C++ という本を読んだんだけど書き方がわからないのでひげぽんのコードを参考に勉強しようとしたんだけど日記にコードは殆ど載ってなくてつかえねーと思いつつ書いた。そういう訳であんまり C++ の勉強にはなってないような。ポインタとか参照とかリソース管理とかがさっぱりわからない。メモリが駄々漏れ。
以下ソースコード
main
#include <iostream> #include <scheme.h> using namespace std; using namespace scheme; int main() { Environment* env = createGlobalEnvironment(); string str, line; while (getline(cin, line)) { str += line; if(Expression* exp = parse(str)) { cout << "; " << exp->eval(env)->string() << endl; str = ""; } } return 0; }
scheme.h
#ifndef SCHEME_H_INCLUDED #define SCHEME_H_INCLUDED #include <map> #include <vector> #include <boost/function.hpp> #include <boost/lambda/lambda.hpp> namespace scheme { class Expression; class Variable; typedef std::vector<Expression*> Expressions; // Parse Expression* parse(const std::string& str); // Environment - 変数テーブルをもってる class Environment { public: Environment(Environment* env = 0): base(env) {}; Expression* variableValue(Variable* var); void defineVariable(Variable* var, Expression* value); Environment* extend(Expressions* vars, Expressions* vals); private: std::map<std::string, Expression*> table; Environment* base; }; Environment* createGlobalEnvironment(); // Expressions Expressions* evalAll(Expressions const* exprs, Environment* env); std::string toString(Expressions* exprs); // Expression class Expression { public: virtual ~Expression() {}; virtual Expression* eval(Environment* env) = 0; virtual std::string string() const = 0; virtual bool boolValue() const { return false; } }; class Number: public Expression { public: Number(int n): num(n) {}; virtual Expression* eval(Environment* env); virtual std::string string() const; virtual bool boolValue() const; int intValue() const; private: int num; }; Number* True = new Number(1); Number* False = new Number(0); class String: public Expression { public: String(const std::string& s): str(s) {}; virtual Expression* eval(Environment* env); virtual std::string string() const; private: std::string str; }; class Variable: public Expression { public: Variable(const std::string& str): name_(str) {} virtual Expression* eval(Environment* env); virtual std::string string() const; const std::string name() const; private: std::string name_; }; class If: public Expression { public: If(Expression* p, Expression* c, Expression* a) : predicate(p), consequent(c), alternative(a) {} virtual Expression* eval(Environment* env); virtual std::string string() const; private: Expression* predicate; Expression* consequent; Expression* alternative; }; class Sequence: public Expression { public: Sequence(Expressions* exprs): expressions(exprs) {}; virtual Expression* eval(Environment* env); virtual std::string string() const; private: Expressions* expressions; }; class Define: public Expression { public: Define(Variable* var, Expression* expr): variable(var), value(expr) {} virtual Expression* eval(Environment* env); virtual std::string string() const; private: Variable* variable; Expression* value; }; class Lambda: public Expression { public: Lambda(Expressions* exprs, Expression* b): parameters(exprs), body(b) {} virtual Expression* eval(Environment* env); virtual std::string string() const; private: Expressions* parameters; Expression* body; }; class Procedure: public Expression { public: Procedure(Expressions* params, Expression* expr, Environment* env) : parameters(params), body(expr), environment(env) {} virtual Expression* eval(Environment* env); virtual Expression* apply(Expressions* arguments); virtual std::string string() const; protected: Expressions* parameters; Expression* body; Environment* environment; }; class Operation: public Expression { public: Operation(Expression* opr, Expressions* opd) : op(opr), operands(opd) {} virtual Expression* eval(Environment* env); virtual std::string string() const; protected: Expression* op; Expressions* operands; }; class PrimitiveProcedure: public Procedure { public: PrimitiveProcedure(const std::string& str, boost::function<int (int, int)> f) : Procedure(0, 0, 0), op(str), applyFunction(f) {} virtual Expression* apply(Expressions* arguments); virtual std::string string() const; private: const std::string op; boost::function<int (int, int)> applyFunction; }; using namespace boost::lambda; PrimitiveProcedure* Plus = new PrimitiveProcedure("+", _1 + _2); PrimitiveProcedure* Minus = new PrimitiveProcedure("-", _1 - _2); PrimitiveProcedure* Multiplies = new PrimitiveProcedure("*", _1 * _2); PrimitiveProcedure* Equals = new PrimitiveProcedure("=", _1 == _2); } #endif // SCHEME_H_INCLUDED
scheme.cpp
#include <scheme.h> #include <boost/lexical_cast.hpp> namespace scheme { using namespace std; // Environment Expression* Environment::variableValue(Variable* var) { if(Expression* result = table[var->name()]) return result; if(base) return base->variableValue(var); throw "Unbound variable"; } void Environment::defineVariable(Variable* var, Expression* value) { table[var->name()] = value; } Environment* Environment::extend(Expressions* vars, Expressions* vals) { if(vars->size() != vals->size()) throw "Invalid arguments"; Environment* child = new Environment(this); for (size_t i = 0; i < vars->size(); i++) child->defineVariable(dynamic_cast<Variable*>((*vars)[i]), (*vals)[i]); return child; } Environment* createGlobalEnvironment() { Environment* env = new Environment(); env->defineVariable(new Variable("+"), Plus); env->defineVariable(new Variable("-"), Minus); env->defineVariable(new Variable("*"), Multiplies); env->defineVariable(new Variable("="), Equals); return env; } // Expressions Expressions* evalAll(Expressions const* exprs, Environment* env) { Expressions* evaled = new Expressions(); Expressions::const_iterator from(exprs->begin()), end(exprs->end()); while (from != end) evaled->push_back((*from++)->eval(env)); return evaled; } string toString(Expressions* exprs) { string str; Expressions::const_iterator iter(exprs->begin()), end(exprs->end()); while (iter != end) { str += (*iter++)->string(); if(iter != end) str += " "; } return str; } // Expression Expression* Number::eval(Environment* env) { return this; } string Number::string() const { return boost::lexical_cast<std::string>(num); } bool Number::boolValue() const { return num; } int Number::intValue() const { return num; } Expression* String::eval(Environment* env) { return this; } string String::string() const { return str; } Expression* Variable::eval(Environment* env) { return env->variableValue(this); } string Variable::string() const { return name_; } const std::string Variable::name() const { return name_; } Expression* If::eval(Environment* env) { return predicate->eval(env)->boolValue() ? consequent->eval(env) : alternative->eval(env); } string If::string() const { return "(if " + predicate->string() + " " + consequent->string() + " " + alternative->string() + ")"; } Expression* Sequence::eval(Environment* env) { Expressions::const_iterator iter(expressions->begin()), end(expressions->end()); while (iter != end) { Expression* e = (*iter++)->eval(env); if(iter == end) return e; } throw "empty sequence"; } string Sequence::string() const { return "(begin " + toString(expressions) + ")"; } Expression* Procedure::eval(Environment* env) { return this; } Expression* Procedure::apply(Expressions* arguments) { return body->eval(environment->extend(parameters, arguments)); } string Procedure::string() const { return "<#proc (lambda (" + toString(parameters) + ") " + body->string() + ")>"; } Expression* PrimitiveProcedure::apply(Expressions* arguments) { Expressions::const_iterator begin(arguments->begin()); int first = dynamic_cast<Number*>(*(arguments->begin()))->intValue(); int second = dynamic_cast<Number*>(*(++(arguments->begin())))->intValue(); return new Number(applyFunction(first, second)); } string PrimitiveProcedure::string() const { return "<#proc (" + op + " x y)>"; } Expression* Operation::eval(Environment* env) { return dynamic_cast<Procedure*>(op->eval(env))->apply(evalAll(operands, env)); } string Operation::string() const { return "(" + op->string() + " " + toString(operands) + ")"; } Expression* Lambda::eval(Environment* env) { return new Procedure(parameters, body, env); } string Lambda::string() const { return "(lambda (" + toString(parameters) + ") " + body->string() + ")"; } Expression* Define::eval(Environment* env) { env->defineVariable(variable, value->eval(env)); return new String("ok"); } string Define::string() const { return "(define " + variable->string() + " " + value->string() + ")"; } }
parser
#include <scheme.h> #include <stack> #include <boost/spirit/core.hpp> namespace scheme { using namespace std; using namespace boost::spirit; stack<Expression*> expressions; stack<Expressions*> sequences; // stack operations void clear() { expressions = stack<Expression*>(); sequences = stack<Expressions*>(); } void push(Expression* exp) { expressions.push(exp); } Expression* pop() { Expression* expr = expressions.top(); expressions.pop(); return expr; } void pushToSequence(Expression* exp) { sequences.top()->push_back(exp); } void pushEmptySequence() { sequences.push(new Expressions()); } Expressions* popSequence() { Expressions* params = sequences.top(); sequences.pop(); return params; } // callbacks void pushNumber(int n) { push(new Number(n)); } void pushSymbol(char const* first, char const* last) { push(new Variable(string(first, last))); } void pushBegin(char const* first, char const* last) { push(new Sequence(popSequence())); } void pushCond(char const* first, char const* last) { Expression* alternative = pop(); Expression* consequent = pop(); Expression* predicate = pop(); push(new If(predicate, consequent, alternative)); } void pushEmptySequence_(char const* first, char const* last) { pushEmptySequence(); } void moveToSequence(char const* first, char const* last) { pushToSequence(pop()); } void pushLambda(char const* first, char const* last) { push(new Lambda(popSequence(), pop())); } void pushDefine(char const* first, char const* last) { Expression* value = pop(); Variable* variable = dynamic_cast<Variable*>(pop()); push(new Define(variable, value)); } void pushDefineLambda(char const* first, char const* last) { Expression* body = pop(); Variable* variable = dynamic_cast<Variable*>(pop()); push(new Define(variable, new Lambda(popSequence(), body))); } void pushCall(char const* first, char const* last) { push(new Operation(pop(), popSequence())); } struct SchemeParser: public grammar<SchemeParser> { template<typename S> struct definition { rule<S> s, num, symbol; rule<S> symbol_sequence, sequence; rule<S> begin, cond, lambda, define, define_lambda; rule<S> call, expr; definition(const SchemeParser& self) { s = +space_p; num = int_p[&pushNumber]; symbol = (+(graph_p - '(' - ')' - digit_p))[&pushSymbol]; symbol_sequence = epsilon_p[&pushEmptySequence_] >> !(symbol[&moveToSequence] >> *(s >> symbol[&moveToSequence])); sequence = epsilon_p[&pushEmptySequence_] >> !(expr[&moveToSequence] >> *(s >> expr[&moveToSequence])); begin = "(begin" >> s >> sequence >> ')'; cond = "(if" >> s >> expr >> s >> expr >> s >> expr >> ')'; lambda = "(lambda" >> s >> '(' >> symbol_sequence >> ')' >> s >> expr >> ')'; define = "(define" >> s >> symbol >> s >> expr >> ')'; define_lambda = "(define" >> s >> '(' >> symbol >> s >> symbol_sequence >> ')' >> s >> expr >> ')'; call = '(' >> expr[&pushEmptySequence_] >> *(s >> expr[&moveToSequence]) >> ')'; expr = num | symbol | begin[&pushBegin] | cond[&pushCond] | lambda[&pushLambda] | define[&pushDefine] | define_lambda[&pushDefineLambda] | call[&pushCall]; } const rule<S>& start() const { return expr; } }; }; Expression* parse(const string& str) { clear(); SchemeParser scmp; return parse(str.c_str(), scmp).full ? pop() : 0; } }